Tokens
Generated by scripts/build.mjs from tokens/tokens.json:
every token, as the custom property the CSS declares (dist/tokens.css):
its path in the file, less the layer, prefixed --al-.
Use the semantic tokens: they name what a value is for, so the themes,
Arabic and contrast stay right in one place. The primitives are the
palette they point to; a component uses them only through a role
(principles). A value that's another token is named
without its prefix: color-cloud-98 is --al-color-cloud-98. Where a
mode has its own value, it's beside
the light theme's: dark, under [data-theme="dark"] or the system's setting,
and Arabic, under :lang(ar).
Semantic
Colour
Roles. Most roles point to a primitive; the exceptions are literal on purpose: translucent colours (washes, line-strong, the backdrop, pattern-line), which are a hue at an alpha that only makes sense for that role, and the tints, which are a fixed palette of their own. Each is still measured by the contrast check.
| Token | Value | Dark | For |
|---|---|---|---|
--al-color-bg | color-cloud-98 | color-slate-18 | The page. |
--al-color-surface | color-white | color-slate-21 | Cards, panels, dialogs, inputs. |
--al-color-surface-sunk | color-cloud-96 | color-slate-24 | Wells, table stripes, info notices, hover. |
--al-color-text | color-cloud-21 | color-sand-93 | Body text and headings. |
--al-color-text-muted | color-cloud-45 | color-sand-74 | Secondary text, hints, captions. |
--al-color-line | color-cloud-93 | oklch(100% 0 0 / 0.12) | Hairlines that separate (decorative: no contrast minimum). |
--al-color-line-strong | oklch(20.8% 0.042 266 / 0.4) | oklch(100% 0 0 / 0.4) | Section rules and dividers that structure a page. |
--al-color-control | color-slate-62 | Borders of form controls: at least 3:1 against surfaces (WCAG 1.4.11). The same in both themes. | |
--al-color-accent | color-teal-51 | color-teal-83 | The brand accent: primary actions, links on hover, focus, selection; and success, with an icon and a word. |
--al-color-accent-hover | color-teal-43 | color-teal-91 | A primary button, hovered: a clear step from the accent, with on-accent still readable. |
--al-color-on-accent | color-white | color-slate-18 | Text and icons on the accent. |
--al-color-accent-wash | oklch(60% 0.11 183 / 0.12) | oklch(82.5% 0.105 168 / 0.12) | Tints: selected rows, highlighted totals. |
--al-color-focus | color-teal-51 | color-teal-83 | The focus outline. |
--al-color-focus-on-inverse | color-teal-83 | color-teal-51 | The focus outline on the inverse ground (the bulk bar, toasts): the other theme's focus colour. |
--al-color-danger | color-red-54 | color-red-68 | Errors, and actions that destroy. |
--al-color-danger-hover | color-red-46 | color-red-78 | A filled danger button, hovered. |
--al-color-danger-wash | oklch(63% 0.22 27 / 0.1) | oklch(68% 0.17 25 / 0.14) | |
--al-color-warning | color-amber-49 | color-amber-80 | Needs attention soon; experimental. |
--al-color-warning-wash | oklch(86% 0.16 92 / 0.3) | oklch(80% 0.12 85 / 0.12) | |
--al-color-success | color-teal-51 | color-teal-83 | Done, saved, passed: the brand teal, always with an icon and a word, so colour is never the only signal. |
--al-color-success-wash | oklch(60% 0.11 183 / 0.12) | oklch(82.5% 0.105 168 / 0.12) | |
--al-color-info | color-blue-53 | color-blue-80 | Neutral information. |
--al-color-info-wash | oklch(62% 0.19 262 / 0.1) | oklch(80% 0.08 250 / 0.12) | |
--al-color-backdrop | oklch(20.8% 0.042 266 / 0.45) | oklch(0% 0 0 / 0.6) | Behind modal dialogs. |
--al-color-inverse | color-cloud-21 | color-sand-93 | Toasts and tooltips: a dark chip on a light page, and the reverse. |
--al-color-on-inverse | color-cloud-98 | color-slate-18 | |
--al-color-chart-0 | color-cloud-87 | oklch(100% 0 0 / 0.2) | The neutral: the rest, the track. |
--al-color-chart-1 | color-chart-green-light | color-chart-green-dark | |
--al-color-chart-2 | color-chart-orange-light | color-chart-orange-dark | |
--al-color-chart-3 | color-chart-blue-light | color-chart-blue-dark | |
--al-color-chart-4 | color-chart-raspberry-light | color-chart-raspberry-dark | |
--al-color-tint-teal-bg | #CCFBF1 | oklch(70% 0.13 183 / 0.18) | |
--al-color-tint-teal-ink | #115E59 | #5EEAD4 | |
--al-color-tint-green-bg | #DCFCE7 | oklch(72% 0.17 150 / 0.18) | |
--al-color-tint-green-ink | #166534 | #86EFAC | |
--al-color-tint-yellow-bg | #FEF9C3 | oklch(85% 0.17 95 / 0.18) | |
--al-color-tint-yellow-ink | #854D0E | #FDE047 | |
--al-color-tint-orange-bg | #FFEDD5 | oklch(75% 0.16 55 / 0.18) | |
--al-color-tint-orange-ink | #9A3412 | #FDBA74 | |
--al-color-tint-pink-bg | #FCE7F3 | oklch(72% 0.17 350 / 0.18) | |
--al-color-tint-pink-ink | #9D174D | #F9A8D4 | |
--al-color-tint-purple-bg | #F3E8FF | oklch(70% 0.17 305 / 0.2) | |
--al-color-tint-purple-ink | #6B21A8 | #D8B4FE | |
--al-color-tint-blue-bg | #DBEAFE | oklch(68% 0.16 262 / 0.2) | |
--al-color-tint-blue-ink | #1E40AF | #93C5FD | |
--al-color-tint-cyan-bg | #CFFAFE | oklch(75% 0.13 215 / 0.18) | |
--al-color-tint-cyan-ink | #155E75 | #67E8F9 | |
--al-color-syntax-keyword | #7E22CE | #D8B4FE | |
--al-color-syntax-string | #166534 | #86EFAC | |
--al-color-syntax-number | #9A3412 | #FDBA74 | |
--al-color-syntax-comment | #5B6474 | #A3ABB8 | |
--al-color-syntax-function | #1D4ED8 | #93C5FD | |
--al-color-syntax-tag | #0F766E | #5EEAD4 | |
--al-color-syntax-attribute | #9D174D | #F9A8D4 | |
--al-color-pattern-line | oklch(24.5% 0.028 265 / 0.26) | oklch(100% 0 0 / 0.2) | The tessellation's strands, in ink, as on arabesquelabs.com: quiet enough for a heading and lede to sit on in the hero; never behind body text. |
--al-color-pattern-scaffold | oklch(24.5% 0.028 265 / 0.12) | oklch(100% 0 0 / 0.1) | The tiling's polygons, shown as scaffolding while the hero draws itself in, and in the construction figure. |
--al-color-pattern-quiet | oklch(24.5% 0.028 265 / 0.14) | oklch(100% 0 0 / 0.1) | The quiet pattern: a low-contrast field for a small area (an empty state, a card) with nothing on it; never behind text. |
--al-color-pattern-focal | color-accent | The hero's focal tile: its star, and its octagon at reduced opacity. | |
--al-color-pattern-focal-fill | oklch(51% 0.095 183 / 0.16) | oklch(82.5% 0.105 168 / 0.16) | The fill inside the focal tile's star. |
Typefaces
Latin first; each Latin face has no Arabic, so Arabic falls through to its own face in the same stack.
| Token | Value | For |
|---|---|---|
--al-font-sans | Jost, IBM Plex Sans Arabic, Noto Sans Arabic, SF Arabic, Geeza Pro, ui-sans-serif, system-ui, sans-serif | Interface text. |
--al-font-display | Jost, Kufam, Noto Kufi Arabic, IBM Plex Sans Arabic, ui-sans-serif, system-ui, sans-serif | Headings and the wordmark. |
--al-font-serif | Newsreader, Amiri, Noto Naskh Arabic, Scheherazade New, Georgia, serif | Reading text. |
--al-font-mono | IBM Plex Mono, ui-monospace, SFMono-Regular, Menlo, monospace | Code and fixed-width figures. |
Script scale
Multiplies every text size: Arabic is set a little larger to match Latin's apparent size.
| Token | Value | Arabic | For |
|---|---|---|---|
--al-script-scale | 1 | 1.08 | Multiplies every text size: Arabic is set a little larger to match Latin's apparent size. |
Type sizes
The type scale. Each size is multiplied by --al-script-scale when built.
| Token | Value | For |
|---|---|---|
--al-text-xs | 0.8125rem | |
--al-text-sm | 0.9375rem | |
--al-text-base | 1.0625rem | Interface body text. |
--al-text-md | 1.1875rem | Reading text. |
--al-text-lg | 1.375rem | |
--al-text-xl | 1.625rem | |
--al-text-2xl | clamp(1.875rem, 1.6rem + 1.1vw, 2.5rem) | |
--al-text-3xl | clamp(2.25rem, 1.7rem + 2.2vw, 3.375rem) | |
--al-text-display | clamp(2.75rem, 1.9rem + 3.6vw, 5rem) |
Line height
Line heights; Arabic (the ar mode) needs more room for its ascenders, descenders and marks.
| Token | Value | Arabic |
|---|---|---|
--al-leading-tight | 1.1 | 1.35 |
--al-leading-heading | 1.2 | 1.45 |
--al-leading-ui | 1.45 | 1.65 |
--al-leading-prose | 1.6 | 1.9 |
Weight
| Token | Value |
|---|---|
--al-weight-regular | 400 |
--al-weight-medium | 500 |
--al-weight-semibold | 600 |
Letter-spacing
| Token | Value | Arabic | For |
|---|---|---|---|
--al-tracking-tight | -0.015em | 0 | |
--al-tracking-none | 0 | ||
--al-tracking-wide | 0.05em | 0 | The wordmark only. Never on Arabic. |
Space
A 4px step (0.25rem): space-N is N × 4px.
| Token | Value | For |
|---|---|---|
--al-space-0 | 0 | |
--al-space-1 | 0.25rem | |
--al-space-2 | 0.5rem | |
--al-space-3 | 0.75rem | |
--al-space-4 | 1rem | |
--al-space-5 | 1.25rem | |
--al-space-6 | 1.5rem | |
--al-space-8 | 2rem | |
--al-space-12 | 3rem | |
--al-space-16 | 4rem | |
--al-space-0-5 | 0.125rem | |
--al-space-1-5 | 0.375rem | |
--al-space-section | clamp(3rem, 2rem + 6vw, 9rem) | Between website sections. |
--al-space-gutter | clamp(1rem, 0.5rem + 3vw, 4rem) | Page side gutter. |
Radius
| Token | Value | For |
|---|---|---|
--al-radius-sm | 0.125rem | Controls, cards: the brand's crisp edge. |
--al-radius-md | 0.25rem | Chart ends, toasts. |
--al-radius-full | 999px |
Border
| Token | Value |
|---|---|
--al-border-hairline | 1px |
--al-border-focus | 2px |
--al-border-focus-offset | 2px |
Size
| Token | Value | For |
|---|---|---|
--al-size-control-sm | 2rem | 32px. |
--al-size-control-md | 2.5rem | 40px. |
--al-size-target-min | 1.5rem | WCAG 2.5.8: every target at least 24 × 24px. |
--al-size-target-touch | 2.75rem | 44px, on coarse pointers. |
--al-size-icon | 1.125rem | |
--al-size-measure | 65ch | The longest line of reading text. |
--al-size-page | 75rem | |
--al-size-dialog-sm | 30rem | |
--al-size-dialog-md | 48rem | |
--al-size-sidebar | 14rem | |
--al-size-sidebar-collapsed | 3.75rem |
Duration
| Token | Value | For |
|---|---|---|
--al-duration-fast | 150ms | Hover, press, feedback. |
--al-duration-base | 250ms | Theme change, disclosure, toasts. |
Easing
| Token | Value |
|---|---|
--al-ease-standard | cubic-bezier(0.2, 0, 0, 1) |
--al-ease-exit | cubic-bezier(0.4, 0, 1, 1) |
Stacking
| Token | Value |
|---|---|
--al-z-sticky | 10 |
--al-z-toast | 100 |
Breakpoints
For JS and Tailwind; CSS media queries use the literal values (custom properties can't go in media queries).
| Token | Value |
|---|---|
--al-breakpoint-sm | 40rem |
--al-breakpoint-md | 52rem |
--al-breakpoint-lg | 64rem |
Pattern
The tessellation's geometry, read by js/pattern.js from the custom properties.
| Token | Value | For |
|---|---|---|
--al-pattern-side | 32px | Polygon side on wide screens. |
--al-pattern-side-narrow | 22px | Polygon side under the md breakpoint. |
--al-pattern-stroke | 1.25px | |
--al-pattern-contact-angle | 67.5 | Degrees. |
--al-pattern-hero-side | 46px | Polygon side of the hero pattern (drawHero) on wide screens, as on arabesquelabs.com. |
--al-pattern-hero-side-narrow | 30px | Polygon side of the hero pattern under the md breakpoint. |
--al-pattern-hero-stroke | 1px | The hero's strands and scaffolding. |
--al-pattern-focal-stroke | 1.6px | The focal tile's star. |
Component
Button
| Token | Value |
|---|---|
--al-button-height | size-control-md |
--al-button-height-sm | size-control-sm |
--al-button-padding-inline | space-4 |
--al-button-radius | radius-sm |
Field
| Token | Value |
|---|---|
--al-field-height | size-control-md |
--al-field-padding-inline | space-3 |
--al-field-radius | radius-sm |
Primitives
Named by hue family and OKLCH lightness (so cloud-98 is about 98% lightness). Brand colours are noted with their logo-pack hex.
Sand
| Token | Value | For |
|---|---|---|
--al-color-sand-74 | oklch(73.5% 0.012 100) | |
--al-color-sand-93 | oklch(92.8% 0.006 100) | Pale #E8E7E3 |
Cloud
Cool, bright neutrals for the light theme (close to Tailwind's slate 50–300, as on mosab.co.uk).
| Token | Value | For |
|---|---|---|
--al-color-cloud-21 | oklch(20.8% 0.042 266) | #0F172A |
--al-color-cloud-45 | oklch(44.6% 0.043 257) | #475569 |
--al-color-cloud-87 | oklch(86.9% 0.022 252) | #CBD5E1 |
--al-color-cloud-93 | oklch(92.9% 0.013 255) | #E2E8F0 |
--al-color-cloud-96 | oklch(96.8% 0.007 248) | #F1F5F9 |
--al-color-cloud-98 | oklch(98.4% 0.003 248) | #F8FAFC |
Slate
| Token | Value | For |
|---|---|---|
--al-color-slate-18 | oklch(18.2% 0.017 265) | Night #0E121A |
--al-color-slate-21 | oklch(21.2% 0.018 265) | |
--al-color-slate-24 | oklch(24.2% 0.018 265) | |
--al-color-slate-62 | oklch(62% 0.012 265) |
Teal
| Token | Value | For |
|---|---|---|
--al-color-teal-43 | oklch(43% 0.08 183) | The light theme's accent, hovered. |
--al-color-teal-51 | oklch(51% 0.095 183) | A brighter teal, near #0F766E: the light theme's accent. |
--al-color-teal-83 | oklch(82.5% 0.105 168) | Mint #7CDCB9 |
--al-color-teal-91 | oklch(91% 0.075 168) | The dark theme's accent, hovered. |
Red
| Token | Value | For |
|---|---|---|
--al-color-red-46 | oklch(46% 0.18 27) | Danger, hovered, in the light theme. |
--al-color-red-54 | oklch(54% 0.21 27) | A brighter red, near #DC2626. |
--al-color-red-68 | oklch(68% 0.17 25) | |
--al-color-red-78 | oklch(78% 0.13 25) | Danger, hovered, in the dark theme. |
Amber
| Token | Value |
|---|---|
--al-color-amber-49 | oklch(49% 0.105 75) |
--al-color-amber-80 | oklch(80% 0.12 85) |
Blue
| Token | Value | For |
|---|---|---|
--al-color-blue-53 | oklch(53% 0.19 262) | A bright blue, near #2563EB. |
--al-color-blue-80 | oklch(80% 0.08 250) |
Chart
Categorical chart hues, in a fixed order, validated for colour-blind separation on both surfaces (dataviz palette validator).
| Token | Value |
|---|---|
--al-color-chart-green-light | #007a5c |
--al-color-chart-orange-light | #c26300 |
--al-color-chart-blue-light | #3d73b6 |
--al-color-chart-raspberry-light | #b8336a |
--al-color-chart-green-dark | #3ba888 |
--al-color-chart-orange-dark | #ca7e31 |
--al-color-chart-blue-dark | #5188cd |
--al-color-chart-raspberry-dark | #d75b8f |
White
| Token | Value |
|---|---|
--al-color-white | oklch(100% 0 0) |