Astryx utilizes a comprehensive CSS custom properties (tokens) engine for colors, elevation, radii, and spatial gaps. In this lesson, we break down the token categories, map them onto the board styles we wrote in lesson 4, and add a light/dark toggle button to the board header.
Files this lesson: src/app/components/KanbanBoard.tsx (rewritten). page.tsx stays untouched.
Token Categories
1. Spacing Scale
Astryx provides a 4px-based spacing scale via CSS variables:
var(--spacing-1)=4pxvar(--spacing-2)=8pxvar(--spacing-3)=12pxvar(--spacing-4)=16pxvar(--spacing-10)=40px
The board uses these everywhere: padding: 'var(--spacing-4)' on the column strip, gap={3} between stacked content, padding={3} on cards.
2. Shadows & Elevation
Elevation shadows communicate hierarchy and interactive states:
var(--shadow-low): Subtle card resting state.var(--shadow-med): Card hover state — this is whatstyles.carduses.var(--shadow-high): Floating drag overlay —styles.floatinguses it.
Card also exposes an elevation prop ('none' | 'low' | 'med' | 'high') that maps to these same tokens — the idiomatic shortcut. We use xstyle with the raw --shadow-* variables to teach the override mechanism, but whenever a built-in prop exists, prefer it.
3. Container Radii
var(--radius-container): Standard container border radius — used by theghostslot instyles.ts.
4. Background Colors
var(--color-background-muted): Used for empty column shells and drop placeholder slots — theghostbackground and the mutedCard variant="muted"columns.
Light & Dark: It’s All One Theme
Astryx themes carry both a light and a dark token set — color tokens are [light, dark] tuples, and the <Theme> provider (which you installed in lesson 1) resolves whichever mode is active. mode="system" follows the OS, which is exactly why the @media (prefers-color-scheme: dark) hover conditionals from lesson 4 already activate on dark-mode machines: no code required.
Forcing a mode lets you test both sets without changing your OS setting. <Theme> accepts mode: 'system' | 'light' | 'dark' and manages data-theme / color-scheme on the DOM, which prefers-color-scheme follows — so your custom StyleX conditionals respond to the toggle with zero extra code.
Adding the Toggle to the Board
usePreferences() from lesson 3 already exposes mode and setMode. Wire a toggle button into the board header that cycles system → light → dark → system:
'use client';
import { Section } from '@astryxdesign/core/Section';import { Layout, LayoutHeader, LayoutContent, HStack, VStack,} from '@astryxdesign/core/Layout';import { Heading, Text } from '@astryxdesign/core/Text';import { Card } from '@astryxdesign/core/Card';import { Button } from '@astryxdesign/core/Button';import { useTranslator } from '@astryxdesign/core/i18n';import { usePreferences } from '../providers';import { styles } from '../styles';
export default function KanbanBoard() { const t = useTranslator(); const { locale, setLocale } = usePreferences(); const { mode, setMode, locale, setLocale } = usePreferences();
return ( <Section height="100dvh"> <Layout height="fill" header={ <LayoutHeader hasDivider padding={4}> <HStack hAlign="between" vAlign="center"> <Heading level={3}>{t('@app.board.title')}</Heading> <HStack gap={2} vAlign="center"> <Text type="supporting" color="secondary" hasTabularNumbers> 4 {t('@app.tasks')} </Text> {/* Language toggle (lesson 3) */} <Button label={t('@app.switchLanguage')} variant="secondary" onClick={() => setLocale(locale === 'en' ? 'fr' : 'en')} /> {/* Theme toggle: system → light → dark → system */} <Button label={ mode === 'system' ? 'Light mode' : mode === 'light' ? 'Dark mode' : 'System mode' } variant="secondary" onClick={() => setMode( mode === 'system' ? 'light' : mode === 'light' ? 'dark' : 'system', ) } /> </HStack> </HStack> </LayoutHeader> } content={ <LayoutContent padding={0}> <HStack gap={4} xstyle={styles.boardColumns}> {[0, 1, 2, 3].map(i => ( <Card key={i} variant="muted" padding={0} xstyle={styles.columnShell} > <VStack gap={3} padding={3}> <Heading level={4}>Column {i + 1}</Heading> <Card padding={3} xstyle={styles.card}> <Heading level={5}>Placeholder task</Heading> <Text type="supporting" color="secondary" maxLines={2}> A placeholder card inside the styled column shell. </Text> </Card> </VStack> </Card> ))} </HStack> </LayoutContent> } /> </Section> );}The button label always shows the next mode: from system it offers Light, from Light it offers Dark, from Dark it returns to System. Watch the whole board — column surfaces, text, borders, and the hover ring — re-theme instantly.
Semantic Variants
Astryx components like StatusDot and Badge accept semantic variant props. These map domain states to theme colors, and they are the vocabulary our board’s column/priority model uses from lesson 6 onward:
| Variant | Purpose | Kanban Board Use Case |
|---|---|---|
neutral | Baseline / default state | To-do column, Task reference ID |
accent | In-progress / primary action | In-progress column status |
warning | Needs attention / review | In-review column status, Medium priority |
success | Completed / resolved state | Done column status |
error | High urgency / blocker | High priority tasks |
teal | Low urgency / info | Low priority tasks |
Variant vocabularies overlap but differ:
accentis aStatusDotvariant. The accent-colored badge variant is namedinfo(it uses the same--color-accenttoken), so a badge equivalent of the “In progress” dot is<Badge variant="info" />.
You’ll see these in action starting in lesson 6, when the board renders real data with priority badges.
See It in Action
Run npm run dev. Click the theme toggle: Light mode forces light even if your OS is dark; Dark mode forces dark on a light OS; System mode hands control back to the OS. Flip to dark and hover a task card — the light ring + deep shadow from styles.card appears, and the language toggle still works independently. Each setting is plain React state in providers.tsx; nothing about the components changes.
What You Built
- A working light/dark/system toggle living in the board UI, driving the
<Theme mode>provider. - An understanding of every token family the board’s stylesheet uses.
- The semantic variant vocabulary that our data model will target in the next lesson.
In the next lesson, we will model the board’s data and render real columns and tasks.