Repository navigation
feat(design): M2 design tokens (warm neutral + ink, Instrument Sans / IBM Plex Sans Arabic) - #11
Merged
Merged
Conversation
- resources/css/tokens.css: plain CSS custom properties (--nx-*) for colour, type, spacing, radius, elevation and motion. No Tailwind or Vue syntax, so the Filament admin can import it unchanged (ADR-0020). Two layers: --nx-palette-* raw values and semantic tokens. Light only; dark mode is a later block that re-points the semantic colours. - Direction (owner decision): premium minimalist, warm neutrals, an ink accent with no hue; colour is reserved for status. - Type: Instrument Sans (Latin and Malay) and IBM Plex Sans Arabic, both self-hosted through the existing laravel-vite-plugin Bunny fonts integration. Arabic is arabic-subset only and not preloaded. :lang(ar) switches the stack and lengthens body line heights. - app.css maps the tokens into Tailwind 4 with @theme inline and clears the default colours, radii, shadows, fonts, type scale and tracking, so every style goes through a token. - Reduced motion sets every duration token to 0ms. - Contrast (NFR-A11Y-03): contrast-pairs.ts derives the required pairs from token names (text 4.5:1; control borders, focus ring and accent 3:1) and a coverage test fails on any colour token that is neither checked nor listed as decorative. WCAG maths and token parsing are first-party (no new dependencies) and unit tested. - vitest.config.ts lets tests import resources/css as ?raw text.
/dev/tokens renders every token read from tokens.css itself: semantic colours and palette, the full contrast table with ratios, the type scale, en/ms/ar samples (Arabic right to left), spacing, radius, elevation and motion. routes/local.php is loaded from bootstrap/app.php only when APP_ENV=local, so the route does not exist in testing, staging, demo or production. The Pest test boots a fresh application per environment to exercise the real gate.
- code-quality.md: token rules (source of truth, cleared Tailwind defaults, semantic-only use, contrast test, local gallery, Arabic). - security.md: start-of-milestone full npm audit for M2 found the same two dev-only advisories; last-reassessed dates updated.
Review finding F1: status backgrounds were only paired with their own
status foreground, so body text inside a tinted alert or badge (e.g. a
danger message in fg-muted) was never checked. Every fg-* token is now
paired with every {tone}-bg at 4.5:1 (8 new pairs, 50 in total), and a
test asserts those pairs exist.
Current values: fg-default about 15.3:1 and fg-muted about 6.2:1 on
every status background. Darkening danger-bg now fails the new
fg-muted pair.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
M2 design tokens: warm neutral palette with an ink accent, Instrument Sans / IBM Plex Sans Arabic, mapped into Tailwind 4. Contrast is enforced by tests, and a token gallery is available only when
APP_ENV=local. No new dependencies.Summary
Five commits on
main(316b311):feat(design): design tokens with a tested contrast floorfeat(design): local-only design token gallerystyle(storefront): the home placeholder uses the tokensdocs(engineering): token rules, plus the start-of-milestone npm audit reassessmenttest(design): body text on status backgrounds (review finding F1)Related Issues
N/A
Requirement
M2 (design tokens), per the Phase 3 milestone plan. NFR-A11Y-03 (contrast ≥ 4.5:1), ADR-0020 (Filament reuses the tokens), ADR-0018 §3 (Arabic uses Latin digits).
Type of Change
What Changed
resources/css/tokens.css: plain CSS custom properties (--nx-*) with no Tailwind or Vue syntax, so Filament can import them unchanged.--nx-palette-*(raw values) and semantic tokens (--nx-color-bg-*,fg-*,accent*,border-*,focus-ring,{success,warning,danger,info}-{fg,bg}).0ms.Owner decisions applied: premium minimalist, warm neutrals, a near-black ink accent with no hue (colour is reserved for status), and Instrument Sans + IBM Plex Sans Arabic.
Fonts:
laravel-vite-pluginBunny integration.arabic-subset only andpreload: false.:lang(ar)switches the stack (Latin words and digits stay in Instrument Sans) and lengthens the body line height. It is scoped by language rather than the page locale, so Arabic content inside an English page gets it too.app.css:@theme inlinemaps the tokens. Tailwind's default colours, radii, shadows, fonts, type scale and tracking are cleared, sobg-red-500and similar classes no longer exist. Adds base canvas/text colours and a:focus-visibleoutline from the focus-ring token.Contrast:
resources/js/design/contrast-pairs.tsderives the required pairs from token names:fgon its ownbgand on every background at 4.5:1;bgat 4.5:1 (body text inside alerts and badges);on-accentonaccentandaccent-hoverat 4.5:1;accent,border-controlandfocus-ringon every background at 3:1 (WCAG 1.4.11).A coverage test fails on any colour token that is neither checked nor listed in
DECORATIVE_COLOR_TOKENSwith a reason. The WCAG maths (contrast.ts) and the token parser (tokens.ts) are first-party and unit tested.Gallery:
routes/local.php(/dev/tokens,Dev/Tokens) is loaded frombootstrap/app.phponly whenAPP_ENV=local.tokens.css?rawitself, so it cannot drift from the stylesheet.vitest.config.ts: lets tests importresources/css/*.cssas?rawtext. By default Vitest stubs CSS, so a raw import is an empty string.Docs:
code-quality.mdgains a "Styling — design tokens" section.security.mdreassessment dates are updated to 2026-11-08 after the fullnpm audit, which found the same two dev-only advisories and nothing new.Design / Architecture
tokens.csswithout Tailwind.@theme inlinemakes utilities reference the token variables at runtime, so the:lang(ar)and reduced-motion overrides apply without separate builds.localthe route does not exist and 404s like any unknown URL. The page chunk (Tokens-*.js, 5.4 kB gzip, token names only) is still built into production assets, but nothing routes to it.Testing
Test Evidence
Local, Node 24.22.2 / npm 11.19.1, PHP 8.5, with
APP_CONFIG_CACHEpointed at a path that doesn't exist, from a cleannode_modules/public/build:npm cinpm run buildcomposer checkcomposer.jsonvalid · 0 advisories · Pint passed · PHPStan 0 errors · Pest 13 passed (47 assertions)npm run typechecknpm testnpm audit --omit=dev@themetokens.css(88 tokens) andapp.css; the built CSS resolvesfont-sans,bg-canvasand others to--nx-*success-fgonbg-mutedat 5.64:1; the lowest non-text pair isborder-controlonbg-mutedat 3.84:1 (needs 3:1)fg-mutedtoneutral-500fails 4 pairs; adding an unclassified--nx-color-new-thingfails the coverage test. Darkeningdanger-bgfails the newfg-mutedondanger-bgpair. All revertedAPP_ENV=localproduction,staging,demoandtesting→ 404;local→ 200, componentDev/Tokensarticle[lang=ar]computesfont-family: "IBM Plex Sans Arabic", "Instrument Sans", …, line-height 28px vs 24px for en; the Plex Arabic woff2 files load;dir=rtlapp-*.js62.80 kB gzip (≤ 200 kB); CSS 5.01 kB gzip; Plex Arabic is not preloadedSecurity / Data Isolation
localDatabase / Migration
Screenshots / Recording
Run
npm run build && php artisan serve, then open/dev/tokenswithAPP_ENV=local. The home page is now the token-styled placeholder. Screenshots can be added on request.Documentation
docs/engineering/code-quality.md,docs/engineering/security.md)Reviewer Notes
tokens.css.@theme inlinereset block inapp.css.contrast-pairs.ts.then:gate inbootstrap/app.php.fontainebuild warning from bunnyoptimizedFallbacksis unchanged.@font-facerules.Node 24 / Vue 3a required check.tokens.css(M6, after ADR-0011).Definition of Done
composer checkpasses locally (validate, audit, Pint, PHPStan, Pest)