diff --git a/Makefile b/Makefile index c298a9e..0650f18 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,7 @@ docs: prepare-web cd contracts && fe doc -o ../web/static/api bundle cd contracts && fe doc -o ../web/static/api static sed -i 's/Fe Docs/Bountiful Docs/g; s/Fe Documentation/Bountiful Documentation/g' web/static/api/index.html + sed -i 's||\n |' web/static/api/index.html # Remove all build artifacts clean: diff --git a/web/static/css/api-theme.css b/web/static/css/api-theme.css new file mode 100644 index 0000000..769a977 --- /dev/null +++ b/web/static/css/api-theme.css @@ -0,0 +1,165 @@ +/* Bountiful C64 theme for the generated /api docs. + Uses the theming knobs exposed by fe-web's styles.css — override the + CSS custom properties and you retheme the whole viewer. */ + +@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Silkscreen:wght@400;700&display=swap'); + +/* Apply C64 palette + pixel rendering in all modes. + Selectors mirror fe-web's so we win on specificity for dark mode. */ +:root, +:root[data-theme="light"], +:root[data-theme="dark"] { + /* Palette */ + --bg: #40318D; + --bg-secondary: #2B1E5C; + --text: #DDD6F3; + --text-muted: #9E90D6; + --border: #7869C4; + --accent: #FFD866; + --accent-hover: #FFEF8F; + --text-accent: #FFD866; + --code-bg: #241750; + + /* Highlight colors — tuned for the purple backdrop */ + --hl-ref-bg: rgba(255, 216, 102, 0.10); + --hl-def-bg: rgba(255, 216, 102, 0.22); + --hl-def-underline: rgba(255, 216, 102, 0.55); + --target-bg: color-mix(in srgb, #FFD866 15%, #2B1E5C); + + /* Root size: bump from the browser default 16px so everything rem-based + scales up together. Silkscreen at small rem values gets illegible. */ +} +html { font-size: 18px; } +:root, :root[data-theme="light"], :root[data-theme="dark"] { + /* Fonts — Silkscreen everywhere */ + --fe-body-font: 'Silkscreen', 'Press Start 2P', monospace; + --fe-heading-font: 'Silkscreen', 'Press Start 2P', monospace; + --fe-mono-font: 'Silkscreen', 'Press Start 2P', monospace; + --fe-code-font: 'Silkscreen', 'Press Start 2P', monospace; + + /* Pixel / retro rendering */ + --fe-heading-weight: 400; + --fe-body-weight: 400; + --fe-sidebar-weight: 400; + --fe-current-weight: 400; + --fe-font-smoothing: none; + --fe-moz-font-smoothing: grayscale; + --fe-text-rendering: geometricPrecision; + --fe-letter-spacing: -0.02em; + /* Simulate bold via text-shadow — widens strokes without changing advance. + --fe-heading-text-shadow applies to h1-h6, --fe-emphasis-text-shadow + applies to the current sidebar item. */ + --fe-heading-text-shadow: 1px 0 0 currentColor; + --fe-emphasis-text-shadow: 1px 0 0 currentColor; + + /* Let content fill; 900px cap leaves empty space with pixel sidebar */ + --fe-content-max-width: none; + + /* Silkscreen is wider per-glyph than proportional sans, so give the + sidebar a bit more room than the 25vw default. 30vw keeps the + sidebar from dominating while still fitting most symbols; items + beyond that width truncate with `…` via the rule below. */ + --fe-sidebar-max-width: 30vw; + + /* fe-code-block shadow-DOM theming (inherits custom props into shadow) */ + --fe-code-bg: #241750; + --fe-code-border: #7869C4; + --fe-code-text: #DDD6F3; + --fe-code-line-number: #7869C4; + --fe-code-size: 0.9rem; + --fe-code-line-height: 1.6; + --fe-code-radius: 3px; + + /* Syntax highlight palette */ + --fe-hl-keyword: #FF79C6; + --fe-hl-type: #FFD866; + --fe-hl-type-interface: #F8C8DC; + --fe-hl-type-variant: #FFB86C; + --fe-hl-function: #8BE9FD; + --fe-hl-string: #A6E22E; + --fe-hl-number: #FFB86C; + --fe-hl-comment: #7869C4; + --fe-hl-operator: #DDD6F3; + --fe-hl-punctuation: #DDD6F3; +} + +/* Match fe-web's dark-mode media-query selector so we win there too */ +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --bg: #40318D; + --bg-secondary: #2B1E5C; + --text: #DDD6F3; + --text-muted: #9E90D6; + --border: #7869C4; + --accent: #FFD866; + --accent-hover: #FFEF8F; + --text-accent: #FFD866; + --code-bg: #241750; + --hl-ref-bg: rgba(255, 216, 102, 0.10); + --hl-def-bg: rgba(255, 216, 102, 0.22); + --hl-def-underline: rgba(255, 216, 102, 0.55); + --target-bg: color-mix(in srgb, #FFD866 15%, #2B1E5C); + } +} + +/* Bountiful-specific UI sugar that isn't covered by fe-web's knobs. + Keep this section thin — everything theme-parameterizable belongs above. */ + +/* Explicit arrow marker for current sidebar item — a11y / colorblind-safe. + Every row reserves an invisible spacer so state changes don't reflow; the + current row just turns the glyph visible. */ +.fe-nav-items a::before, +.fe-nav-mod-name a::before { + content: "▸"; + display: inline-block; + color: transparent; + margin-right: 0.35em; + margin-left: -0.5em; + width: 0.7em; + flex-shrink: 0; +} + +.fe-nav-items li.current > a::before, +.fe-nav-mod-name.current > a::before { + color: var(--accent); +} + +/* Pixel-style drop shadow on page h1 (additive to --fe-heading-text-shadow) */ +article h1, +.item-header h1 { + color: var(--accent); + text-shadow: + 1px 0 0 currentColor, + 2px 2px 0 var(--bg-secondary); +} + +article h2 { + color: var(--accent); + border-bottom: 2px solid var(--border); + padding-bottom: 0.25rem; +} + +/* Member cards: CRT-phosphor inner glow */ +.member-item { + border: 1px solid var(--border); + border-radius: 3px; + background: color-mix(in srgb, var(--bg-secondary) 60%, var(--bg)); + box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 8%, transparent); +} + +/* Truncate long sidebar names with `…` instead of forcing the sidebar + to grow indefinitely. Silkscreen's wide glyphs mean some `test_*` + names exceed 50vw — truncation keeps the sidebar usable; horizontal + scroll inside the sidebar (overflow-x: auto from fe-web) remains as + the fallback if the user wants to see the full name. */ +.fe-nav-items a { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; +} + +.fe-nav-items a > :last-child { + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; +} diff --git a/web/static/css/c64.css b/web/static/css/c64.css index f453bd0..e69c34c 100644 --- a/web/static/css/c64.css +++ b/web/static/css/c64.css @@ -5,13 +5,13 @@ --c64-text: #DDD6F3; --c64-white: #FFFFFF; --c64-dark: #2B1E5C; - --font: 'Press Start 2P', monospace; + --font: 'Silkscreen', monospace; /* Fe web component theming */ --fe-code-bg: #2B1E5C; --fe-code-border: #7869C4; --fe-code-text: #C8BFE7; - --fe-code-font: 'Press Start 2P', monospace; + --fe-code-font: 'Silkscreen', monospace; --fe-code-size: 0.6rem; --fe-code-line-height: 1.8; --fe-code-line-number: #7869C4; @@ -34,7 +34,7 @@ /* ── BASE ── */ html { - font-size: 14px; + font-size: 18px; } body { @@ -42,8 +42,35 @@ body { font-family: var(--font); color: var(--c64-text); line-height: 1.8; + letter-spacing: -0.06em; height: 100vh; overflow: hidden; + -webkit-font-smoothing: none; + -moz-osx-font-smoothing: grayscale; + font-smooth: never; + text-rendering: geometricPrecision; + /* Blend scrollbars with the C64 palette */ + scrollbar-width: thin; + scrollbar-color: var(--c64-border) var(--c64-bg); +} + +/* Webkit scrollbars — match the CRT palette */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} +::-webkit-scrollbar-track { + background: var(--c64-bg); +} +::-webkit-scrollbar-thumb { + background: var(--c64-border); + border-radius: 2px; +} +::-webkit-scrollbar-thumb:hover { + background: var(--c64-text); +} +::-webkit-scrollbar-corner { + background: var(--c64-bg); } /* ── CRT BORDER / FRAME ── */ @@ -92,6 +119,7 @@ body { .c64-header .stars { color: var(--c64-white); + font-family: 'Press Start 2P', monospace; font-size: 1rem; margin-bottom: 4px; } @@ -228,44 +256,6 @@ ol li::before { flex-direction: column; } -/* ── DOC DETAILS (fields & impls in source viewer) ── */ -.doc-fields, -.doc-impl { - margin-top: 12px; - border-top: 1px solid var(--c64-dark); - padding-top: 8px; -} - -.doc-field, -.doc-method { - padding: 6px 8px; - margin-bottom: 4px; - border-left: 2px solid var(--c64-border); -} - -.doc-sig { - font-family: var(--font); - font-size: 0.65rem; - color: var(--c64-text); - word-break: break-all; -} - -.doc-sig .sig-link { - color: var(--c64-white); - text-decoration: none; -} - -.doc-sig .sig-link:hover { - text-decoration: underline; -} - -.doc-desc { - font-size: 0.65rem; - margin-top: 4px; - margin-bottom: 0; - opacity: 0.7; -} - /* ── GLOBAL FOOTER ── */ .c64-footer { display: flex; @@ -557,8 +547,8 @@ ol li::before { display: block; } -/* ── RESPONSIVE: single column on small screens ── */ -@media (max-width: 768px) { +/* ── RESPONSIVE: single column on narrow viewports ── */ +@media (max-width: 1024px) { body { height: auto; overflow: auto; @@ -586,7 +576,7 @@ ol li::before { } html { - font-size: 12px; + font-size: 22px; } .puzzle-board { diff --git a/web/templates/base.html b/web/templates/base.html index b55f321..eb55619 100644 --- a/web/templates/base.html +++ b/web/templates/base.html @@ -7,7 +7,7 @@ - +