diff --git a/.mise.toml b/.mise.toml index 30f67a4..8fff695 100644 --- a/.mise.toml +++ b/.mise.toml @@ -31,11 +31,6 @@ description = "Regenerate docs/src/data/repos.json from the Lab271 GitHub org" dir = "docs" run = "node scripts/refresh-repos.mjs" -[tasks.docs-marks] -description = "Regenerate src/assets/logo.svg and badge.svg (Inter outlined to paths)" -dir = "docs" -run = "uv run scripts/refresh-marks.py" - [tasks.docs-icons] description = "Re-export the favicon/apple-touch-icon PNGs from src/assets/badge.svg" dir = "docs" diff --git a/AGENTS.md b/AGENTS.md index 4dc7161..eef9649 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,7 +23,6 @@ mise run docs-dev # dev server at http://localhost:4321/ mise run docs-check # astro check (type/content check) mise run docs-build # static build into docs/dist mise run docs-repos # regenerate docs/src/data/repos.json from the Lab271 org (needs `gh` auth) -mise run docs-marks # regenerate the logo/badge SVGs (needs uv) mise run docs-icons # re-export the favicon PNGs from src/assets/badge.svg (needs librsvg) ``` @@ -38,11 +37,12 @@ When starting the dev server directly (not via mise), use background mode: - `src/intro.md`: the intro paragraphs, imported into the landing page. - `src/styles/custom.css`: Lab271 brand theme (colors, fonts) mapped onto Starlight's CSS custom properties. - - `src/assets/logo.svg`: the horizontal wordmark used in the nav bar. + - `src/assets/logo.svg`: the horizontal wordmark used in the nav bar. Vendored + from the design team's final artwork (the Lab271 design refresh) rather than + generated - see Branding below. - `src/assets/badge.svg`: the badge mark, and the master for the favicon PNGs - in `public/`. Both SVGs are generated by `scripts/refresh-marks.py` - (`mise run docs-marks`); the PNGs by `scripts/refresh-icons.sh` - (`mise run docs-icons`). + in `public/`. Also vendored artwork. The PNGs are re-exported from it with + `scripts/refresh-icons.sh` (`mise run docs-icons`). - `src/assets/hero.jpg`: the hero photo. - `src/data/repos.json`: committed, auto-generated list of public `Lab271` repos that carry an open source license. Regenerate with `mise run @@ -63,23 +63,33 @@ When starting the dev server directly (not via mise), use background mode: ## Branding -The theme implements the Lab271 brand kit (`labs-branding`, in particular -`brand/visual-identity.md` and `brand/tokens/lab271.tokens.json`). When touching -colors, type or the marks, read those first. The rules that shaped what is here: +The theme implements the Lab271 design refresh (see ADR-0005 in +`Lab271/www.lab271.io`), which supersedes the `labs-branding` +(`brand/visual-identity.md`, `brand/tokens/lab271.tokens.json`) Navy/Primary +Blue/Teal system this site carried before. `labs-branding` has not been +re-derived from the refresh yet, so it is temporarily stale - treat this +repo's `custom.css` and its `www.lab271.io` sibling as authoritative in the +meantime. The rules that shaped what is here: -- **Palette.** Navy `#1A1F36` anchors dark surfaces, including the nav bar in - both color modes. Primary Blue `#6C8EEF` is the signature, Blue Light - `#A8C0FF` its gradient partner. Teal `#36C5B0` is a spark: at most one per - composition, which here is the rule in the badge. Never pure black. +- **Palette.** Near-black `#020C17` anchors dark surfaces, including the nav + bar in both color modes (never pure black). Cyan `#1EE8ED` (dark mode) / + `#18B9BD` (light mode, darkened for contrast) is the signature accent. + Orange `#FF7000`/`#FF8D33` is a spark: at most one per composition, which + here is the rule in the badge. Light mode's canvas is a warm paper off-white + (`#FAF9F5`), not a cool gray. - **Type.** Inter and JetBrains Mono are the open stand-ins for TT Interphases and TT Interphases Mono, the licensed SBP faces that cannot ship on a public - build. Sentence case headings with tight tracking. Mono is "a pinch of tech", - so it is limited to the `e = 2.71828` motif and the language labels. -- **Marks.** `logo.svg` and `badge.svg` set Inter outlined to paths, so they - render without the font installed. The badge follows the brand kit's concept A, - "the door plate". Change them by editing `scripts/refresh-marks.py` and - re-running it, not by editing the path data. -- **Written "Lab271"**, sentence case, no space. Never "LAB271". + build - unchanged by the refresh. Sentence case headings with tight + tracking. Mono is "a pinch of tech", so it is limited to the `e = 2.71828` + motif and the language labels. +- **Marks.** `logo.svg` and `badge.svg` are vendored directly from the design + team's final artwork for the refresh, not generated - the previous + Inter-outlined-to-paths approach (`scripts/refresh-marks.py`) is retired. + Update them by replacing the SVG file with a new export from the design + source, not by hand-editing the path data. +- **Written "Lab271"**, sentence case, no space, in all prose and copy. Never + "LAB271" in running text - the all-caps treatment is reserved for the + wordmark graphic itself (`logo.svg`). - US English throughout. - No em dashes in the copy. `docs/scripts/refresh-repos.mjs` normalizes them out of upstream GitHub descriptions too. diff --git a/docs/public/apple-touch-icon.png b/docs/public/apple-touch-icon.png index 17006ee..042d963 100644 Binary files a/docs/public/apple-touch-icon.png and b/docs/public/apple-touch-icon.png differ diff --git a/docs/public/favicon-32x32.png b/docs/public/favicon-32x32.png index c5c2aa7..d09d114 100644 Binary files a/docs/public/favicon-32x32.png and b/docs/public/favicon-32x32.png differ diff --git a/docs/public/favicon.png b/docs/public/favicon.png index a14ea23..5c375c8 100644 Binary files a/docs/public/favicon.png and b/docs/public/favicon.png differ diff --git a/docs/scripts/refresh-marks.py b/docs/scripts/refresh-marks.py deleted file mode 100755 index 5be3c51..0000000 --- a/docs/scripts/refresh-marks.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env -S uv run --script -# /// script -# requires-python = ">=3.12" -# dependencies = ["fonttools>=4.60", "brotli>=1.1"] -# /// -"""Generate the Lab271 wordmark and badge SVGs. - -Both marks set text in Inter, the brand kit's open stand-in for TT Interphases. -Rather than relying on Inter being installed wherever the SVG is rendered, this -outlines the glyphs to paths, which is what labs-branding's logo/README.md asks -for before an SVG ships. - -Run via `mise run docs-marks`, then `mise run docs-icons` to re-export the -favicon PNGs from the new badge. - -Palette and lockup rules: labs-branding, brand/visual-identity.md sections 1-3. -""" - -from __future__ import annotations - -import urllib.request -from pathlib import Path - -from fontTools.misc.transform import Transform -from fontTools.pens.boundsPen import BoundsPen -from fontTools.pens.svgPathPen import SVGPathPen -from fontTools.pens.transformPen import TransformPen -from fontTools.ttLib import TTFont - -# Inter, latin subset, one file per weight. Pinned to a major version so the -# outlines don't shift under us without a deliberate bump. -FONT_URLS = { - 600: "https://cdn.jsdelivr.net/npm/@fontsource/inter@5/files/inter-latin-600-normal.woff2", - 700: "https://cdn.jsdelivr.net/npm/@fontsource/inter@5/files/inter-latin-700-normal.woff2", -} - -NAVY = "#1A1F36" -BLUE = "#6C8EEF" -BLUE_LIGHT = "#A8C0FF" -TEAL = "#36C5B0" -WHITE = "#FFFFFF" - -HERE = Path(__file__).resolve().parent -ASSETS = HERE.parent / "src" / "assets" -CACHE = HERE / ".font-cache" - - -def font(weight: int) -> TTFont: - CACHE.mkdir(exist_ok=True) - path = CACHE / f"inter-{weight}.woff2" - if not path.exists(): - print(f"downloading {FONT_URLS[weight]}") - with urllib.request.urlopen(FONT_URLS[weight]) as response: - path.write_bytes(response.read()) - return TTFont(path) - - -def run(weight: int, text: str, size: float, tracking: float = 0.0, x: float = 0.0, y: float = 0.0): - """Outline `text` at `size`, returning (path data, advance width, ink bounds). - - `y` is the baseline; `tracking` is extra space per glyph, in the same units - as `size`. No shaping or kerning: the marks are short and set with explicit - tracking anyway. - """ - face = font(weight) - scale = size / face["head"].unitsPerEm - cmap = face.getBestCmap() - glyphs = face.getGlyphSet() - widths = face["hmtx"] - - pen_x = x - paths = [] - bounds = BoundsPen(glyphs) - for char in text: - name = cmap[ord(char)] - placement = Transform(scale, 0, 0, -scale, pen_x, y) - svg = SVGPathPen(glyphs, ntos=lambda value: f"{value:.2f}") - glyphs[name].draw(TransformPen(svg, placement)) - if svg.getCommands(): - paths.append(svg.getCommands()) - glyphs[name].draw(TransformPen(bounds, placement)) - pen_x += widths[name][0] * scale + tracking - # the trailing tracking is space after the last glyph, not part of the run - return " ".join(paths), pen_x - x - tracking, bounds.bounds - - -def wordmark() -> str: - """Horizontal lockup: white "Lab", blue-light "271", tight tracking.""" - size, tracking, baseline = 56, -1.1, 100.0 - lab, lab_width, lab_bounds = run(700, "Lab", size, tracking, 0, baseline) - num, _, num_bounds = run(700, "271", size, tracking, lab_width + tracking, baseline) - - x0 = min(lab_bounds[0], num_bounds[0]) - y0 = min(lab_bounds[1], num_bounds[1]) - x1 = max(lab_bounds[2], num_bounds[2]) - y1 = max(lab_bounds[3], num_bounds[3]) - - return f""" - - - - -""" - - -def badge() -> str: - """Concept A, "the door plate": tracked "Lab" over a large "271", teal rule.""" - side, radius = 512, 117 # radius keeps concept A's 32-at-140px proportion - _, lab_width, _ = run(600, "Lab", 80, 14.6) - _, num_width, _ = run(700, "271", 212, -4) - # centre both lines, ignoring the trailing tracking on "Lab" - lab, _, _ = run(600, "Lab", 80, 14.6, (side - lab_width) / 2, 168) - num, _, _ = run(700, "271", 212, -4, (side - num_width) / 2, 372) - - rule_width, rule_height = 190, 22 - return f""" - - - - - - -""" - - -for name, svg in (("logo.svg", wordmark()), ("badge.svg", badge())): - (ASSETS / name).write_text(svg) - print(f"wrote {ASSETS / name}") diff --git a/docs/src/assets/badge.svg b/docs/src/assets/badge.svg index 3530118..c4aaa18 100644 --- a/docs/src/assets/badge.svg +++ b/docs/src/assets/badge.svg @@ -1,11 +1,12 @@ - - - - - - + + + + + + + + + + + diff --git a/docs/src/assets/logo.svg b/docs/src/assets/logo.svg index cd09630..8cdecb0 100644 --- a/docs/src/assets/logo.svg +++ b/docs/src/assets/logo.svg @@ -1,8 +1,11 @@ - - - - + + + + + + + + + + diff --git a/docs/src/styles/custom.css b/docs/src/styles/custom.css index e1c6f35..0b69d83 100644 --- a/docs/src/styles/custom.css +++ b/docs/src/styles/custom.css @@ -1,47 +1,54 @@ /* Lab271 theme, mapped onto Starlight's design tokens. Palette source: the - Lab271 brand kit (labs-branding, brand/tokens/lab271.tokens.json) - Navy - #1A1F36 (anchor), Primary Blue #6C8EEF (the signature), Blue Light #A8C0FF - (gradient partner), Teal #36C5B0 (the one "spark" per composition), Off-white - #F8F9FB. Dark mode's page background is SBP Full black #020C17, the near-black - the brand kit sanctions in place of pure black, with Navy on top of it as the - nav bar. Starlight derives all surfaces/text/borders from an accent ramp - (low/accent/high), a gray ramp (1=lightest..6/7=darkest in dark mode, - inverted in light) and black/white, so overriding those restyles the whole - site. Dark mode is `:root`; light mode is `:root[data-theme='light']`. */ + Lab271 design refresh (see ADR-0005 in Lab271/www.lab271.io, which supersedes + the Navy/Primary Blue/Teal system this file used to carry from labs-branding) + - near-black #020C17 anchors dark surfaces including both the nav bar and the + canvas itself, cyan #1EE8ED/#18B9BD is the signature accent, orange + #FF7000/#FF8D33 is the one "spark" per composition (the badge's accent rule), + and light mode moves off cool off-white onto the refresh's warm paper canvas + (#FAF9F5). NOTE: labs-branding's brand/tokens/lab271.tokens.json still + reflects the old Navy/Blue/Teal system as of this change - it has not been + re-derived yet, so it is temporarily NOT the source of truth for this + palette; treat this file and its www.lab271.io sibling as authoritative + until labs-branding catches up. Starlight derives all surfaces/text/borders + from an accent ramp (low/accent/high), a gray ramp (1=lightest..6/7=darkest + in dark mode, inverted in light) and black/white, so overriding those + restyles the whole site. Dark mode is `:root`; light mode is + `:root[data-theme='light']`. */ :root { /* Lab271 Dark */ - --sl-color-accent-low: #232a52; - --sl-color-accent: #6c8eef; - --sl-color-accent-high: #a8c0ff; + --sl-color-accent-low: #0d2f34; + --sl-color-accent: #1ee8ed; + --sl-color-accent-high: #8ff3f6; --sl-color-white: #ffffff; /* headings */ --sl-color-gray-1: #eef1f8; --sl-color-gray-2: #c5cbdb; /* body text (fg) */ - --sl-color-gray-3: #9ba3b5; /* muted (grey-400) */ - --sl-color-gray-4: #5f6878; /* dim (grey-600) */ - --sl-color-gray-5: #333a55; /* borders / hairlines */ - --sl-color-gray-6: #1a1f36; /* nav & sidebar bg (Navy) */ - --sl-color-black: #020c17; /* page bg - SBP Full black, the sanctioned near-black */ + --sl-color-gray-3: #8aa0b8; /* muted */ + --sl-color-gray-4: #5f6878; /* dim */ + --sl-color-gray-5: #12263f; /* borders / hairlines */ + --sl-color-gray-6: #020c17; /* nav & sidebar bg - same near-black as the canvas */ + --sl-color-black: #020c17; /* page bg - SBP Full black */ } :root[data-theme='light'] { /* Lab271 Light */ - --sl-color-accent-low: #e9eefc; /* soft periwinkle wash */ - --sl-color-accent: #6c8eef; - --sl-color-accent-high: #3a57c0; /* darkened from blue-dark for link contrast on white */ - --sl-color-white: #1a1f36; /* headings (fg), Navy */ - --sl-color-gray-1: #232942; - --sl-color-gray-2: #2d3142; /* body text (grey-800) */ - --sl-color-gray-3: #5f6878; /* dim (grey-600) */ - --sl-color-gray-4: #9ba3b5; /* faint (grey-400) */ - --sl-color-gray-5: #e2e5eb; /* borders (grey-200) */ - --sl-color-gray-6: #eef1f8; - --sl-color-gray-7: #f8f9fb; /* Off-white */ - --sl-color-black: #ffffff; /* page bg */ + --sl-color-accent-low: #e8f2fd; /* soft blue wash */ + --sl-color-accent: #18b9bd; /* darkened cyan for AA text contrast on white/paper */ + --sl-color-accent-high: #128b8e; + --sl-color-white: #020c17; /* headings (fg) - near-black, not navy */ + --sl-color-gray-1: #1c2b3a; + --sl-color-gray-2: #3d4c5c; /* body text */ + --sl-color-gray-3: #6b7280; /* dim */ + --sl-color-gray-4: #9ba3b5; /* faint */ + --sl-color-gray-5: #e5e0d3; /* borders - warm-tinted, matching the paper canvas */ + --sl-color-gray-6: #f7f5ef; /* panel */ + --sl-color-gray-7: #faf9f5; /* canvas-2 */ + --sl-color-black: #faf9f5; /* page bg - warm paper canvas, not cool off-white */ } /* Fonts: Inter and JetBrains Mono are the brand kit's open stand-ins for TT Interphases and TT Interphases Mono (licensed faces that can't ship on a - public repo/build). Bold headings with tight tracking, 1.5 line-height body, - mono as "a pinch of tech". Webfonts loaded in astro.config.mjs's `head`. */ + public repo/build) - unchanged by the refresh. Bold headings with tight + tracking, 1.5 line-height body, mono as "a pinch of tech". Webfonts loaded + in astro.config.mjs's `head`. */ :root { --sl-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; --sl-font-mono: 'JetBrains Mono', 'Fira Code', Consolas, monospace; @@ -53,15 +60,16 @@ h3 { letter-spacing: -0.02em; } -/* Brandbook: Navy is the anchor for dark surfaces, so the nav bar is Navy in - both color modes and the logo has a single variant (white wordmark, blue +/* Near-black is the anchor for dark surfaces, so the nav bar stays near-black + in both color modes and the logo has a single variant (white wordmark, cyan "271" - see src/assets/logo.svg). Scoped to the header so descendants (social icons, theme select) pick up these overridden tokens instead of the page's - light/dark ones, which would otherwise be low-contrast against a navy bar. */ + light/dark ones, which would otherwise be low-contrast against a near-black + bar. */ header.header { - --sl-color-bg-nav: #1a1f36; - --sl-color-hairline-shade: rgba(255, 255, 255, 0.2); - --sl-color-text-accent: #a8c0ff; + --sl-color-bg-nav: #020c17; + --sl-color-hairline-shade: rgba(255, 255, 255, 0.14); + --sl-color-text-accent: #1ee8ed; --sl-color-white: #ffffff; --sl-color-gray-1: #ffffff; --sl-color-gray-2: #eef1f8; @@ -136,7 +144,7 @@ header.header { .card-link:hover .card { transform: translateY(-3px); border-color: var(--sl-color-text-accent); - box-shadow: 0 0.5rem 1rem -0.5rem rgba(26, 31, 54, 0.35); + box-shadow: 0 0.5rem 1rem -0.5rem rgba(2, 12, 23, 0.35); } /* The repository list: two columns of name / language / description, denser