Transcript
[00:00] So there are a whole bunch of design tokens available to us in the Astryx design system, and they fall into a few categories. The first one being spacing. So it is, let me double check. It is based on a four pixel system. So spacing one is going to be four pixels. Spacing two is going to be eight pixels. Spacing four is going to be 16 pixels. The other one you get is shadows. So there’s three of these are shadow low, medium and high, and that’s going to tweak how the shadow on an element looks. So right now, it’s a little hard to see here, but this is a shadow medium when I hover over these guys. And these are the cards.
[00:48] Everything has every Astryx theme has a light and a dark schema. And what I’m doing right here is I’m making sure that I’m matching the system. If it’s set to system, then we want to get whatever this user system prefers. Outside of that, it’s mostly colors. So we’ve got color background muted, yada, yada, yada. And then there’s also radius container, which is going to be, you know, border radius on a container. Yeah. So what we’re going to do in this lesson is we’re going to implement a toggle, very similar to our language toggles. So we built this guy previously. We’re going to set one there for toggling through system light and dark.
[01:29] So we’re going to jump over here to our Kanban component. And right here after locale and set locale, we’re going to drop in mode, set mode, locale and set locale from use preferences, which we created previously in our providers. Just to remind you on that here is our use preferences function. And it’s managing these app preferences, which have mode, set mode, locale and set locale. So we’re setting all those here. So let’s find our language switch button. So here’s that button there and below this, we’re going to drop a new button. So we’ve got a label and we’re just cycling through. If the mode is system or light mode or light show dark mode and so on a variant of secondary.
[02:17] So again, one of those keys based on the, that controls the color of the text. And then we’ve got an on click that says, you know, which one to cycle to next. So it’s just a bunch of messy ternaries here, but nothing too complex. So we’ll save that guy. And I broke something. You know what? I have this twice. We don’t need that one. So we are looking at a dark mode right now or a system. I’m not quite sure. I’m going to hit light mode and everything’s light. And now you can really see those shadows pop. We’ll go to dark mode and when I hit system mode, of course, it’s going to be the same thing. So we can cycle through these guys. It took very little code, very little effort. It’s all built in. It’s automatic. It’s a very cool system that they have with this Astryx design system.
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.