إنتقل إلى المحتوى الرئيسي

Colour

Colours are named for their role, never their hue: --al-color-accent, not --al-teal. A role has one value per theme; components use roles only. Primitives (--al-color-cloud-98, --al-color-teal-51…) exist to build roles and are not used directly in components. Every primitive is used by a role (tests/system.test.js checks it).

On its own page, in English and Arabic

Roles​

The values below are the primitives each role points to, from tokens/tokens.json; a percentage is a translucent colour.

TokenUse it forLightDark
bgThe pagecloud 98Night (slate 18)
surfaceCards, panels, inputs, menus, dialogswhiteslate 21
surface-sunkWells, hover, table head, affixescloud 96slate 24
textBody text, headingscloud 21Pale (sand 93)
text-mutedHints, labels of secondary things, axes; stale figurescloud 45sand 74
lineHairlines: dividers, card borders, table rulescloud 93white 12%
line-strongDividers that need to be seen (selected, dashed frames)cloud 21 at 40%white 40%
controlThe border of anything you operate: inputs, buttons, checkboxesslate 62slate 62
accentPrimary actions, links, focus, the selected tab; successteal 51Mint (teal 83)
accent-hoverA primary button, hoveredteal 43teal 91
on-accentText on an accent or danger fillwhiteslate 18
accent-washSelected rows, highlighted totalsteal 12%mint 12%
focusThe focus outlineteal 51Mint
focus-on-inverseThe focus outline on the inverse ground (bulk bar, toasts)Mintteal 51
danger, danger-washErrors, and actions that destroyred 54red 68
danger-hoverA filled danger button, hoveredred 46red 78
warning, warning-washNeeds attention; unsavedamber 49amber 80
success, success-washDone, saved (the accent)teal 51Mint
info, info-washNeutral informationblue 53blue 80
inverse, on-inverseToasts, tooltips, the bulk barcloud 98 on cloud 21slate 18 on Pale
backdropBehind a modal dialogcloud 21 at 45%black 60%
chart-1…4Chart series, in this ordersee below
chart-0The neutral: tracks and grounds, never a series of datacloud 87white 20%
tint-*-bg, tint-*-inkTags, highlights, avatars: identity, never statussee tint.md
syntax-*Code: keyword, string, number, comment, function, tag, attribute; each says what kind of word it ispurple, green, orange, grey, blue, teal, raspberry (inks)their pale counterparts
pattern-lineThe tessellation's strands, in ink as on arabesquelabs.comcloud 21 at 26%white 20%
pattern-scaffoldThe tiling's polygons: the hero's scaffolding while it draws in, and the construction figurecloud 21 at 12%white 10%
pattern-focalThe hero's focal tilethe accentthe accent
pattern-focal-fillInside the focal tile's starteal at 16%mint at 16%

All tokens are prefixed: var(--al-color-surface).

Rules​

  • Contrast is computed, not judged. scripts/build.mjs measures every pair the system relies on, in both themes, and the build fails if any falls short: 4.5:1 for text, 3:1 for control borders, focus (on every ground it can be drawn on, the inverse one too) and chart marks. Translucent grounds (washes) are measured on both the page and the surface, and the worse counts. The results are in ../contrast.md.
  • Hairlines aren't boundaries. line is decoration (1.3:1), so anything a person has to find — an input, a checkbox, a button — is drawn in control (3:1 or better against every ground).
  • Syntax colour means something. In code, a colour says what kind of word it is (a keyword, a string, a name), the same in every sample on every page, so a reader learns it once. Only the syntax-* roles colour code, and each is measured on the code's ground, the surface and a marked line.
  • Colour is never the only signal. Every status has an icon and a word ("Saved", "Couldn't save"); errors have an icon and text; chart series have a key, and their values are in a table or tooltip.
  • Success shares the accent. Teal is the brand's one colour; "done" and "go" are the same idea here. It's always paired with a check icon and a word, so it never reads as a mere link.
  • No shadows. Depth comes from the surface steps (bg → surface → surface-sunk) and from borders.
  • One accent per view. The primary button, the current tab and links share it; don't add a second brand colour for emphasis.
  • Washes are tints of their role (danger-wash under a danger notice), and only behind text in the same role or text.

Themes​

Light is the default. Dark follows prefers-color-scheme: dark, and data-theme="light|dark" on <html> overrides the system both ways (js/theme.js remembers the choice as al-theme). Dark is designed, not inverted: the accent lightens to Mint, danger and warning lighten to keep their contrast, and hairlines become translucent white so they sit on any surface.

With prefers-contrast: more, secondary text becomes body text, hairlines become control borders and focus thickens to 3px (css/base.css). With forced-colors: active, components fall back to system colours and keep their borders and focus (each component's CSS has its own rule where needed).

Charts​

The series colours come from hunt's palette, checked for colour-vision deficiency (adjacent pairs ≥ 8 ΔE in OKLab under protanopia, deuteranopia and tritanopia) with the dataviz validator, in each theme against its surface:

OrderLightDark
1 green#007a5c#3ba888
2 orange#c26300#ca7e31
3 blue#3d73b6#5188cd
4 raspberry#b8336a#d75b8f
0 track (not data)cloud 87white 20%

Assign them in this order and never cycle: a fifth series folds into "Other", which takes the next series colour, not chart-0. chart-0 is too light to be read as data (it's under 3:1), so it's only for tracks and grounds. In markup, a mark's series is data-series="1" to "4" (see chart.md). A series keeps its colour when others are filtered out. Status colours (danger, warning, success) are never chart series.