Design system
Every specimen on this page renders the real component from src/components with its import path. Switch the theme to review both — dark is primary, light is complete.
docs/02-design-system.md · Page contract: web/CONVENTIONS.mdEmail templatesFoundations
Design tokens live in src/app/globals.css as CSS variables and are exposed to Tailwind v4 via @theme inline. Never hard-code a colour, shadow or font in a component.
Colour
Every colour is a CSS variable mapped into Tailwind via @theme inline, so utilities and alpha modifiers (bg-accent/10) work in both themes. Dark is the primary theme; light is complete. Never use raw hex in components. Accent text on surfaces is always text-accent-text. Status colours always ship with an icon and a label.
src/app/globals.cssTheme tokens
Left swatch = light, right swatch = dark. Contrast is measured against surface.
Surfaces
- background
var(--background)bg-backgroundL #f7f8fa · D #0b0d10App canvas behind everything - surface
var(--surface)bg-surfaceL #ffffff · D #111418Cards, popovers, dialogs, sidebar - surface-2
var(--surface-2)bg-surface-2L #f2f4f7 · D #171b21Insets, hover rows, selected nav, secondary buttons - surface-3
var(--surface-3)bg-surface-3L #eaedf1 · D #1e232bPressed states, progress tracks, count pills
Borders
- border
var(--border)border-borderL #e5e7eb · D #252a321px hairlines, dividers, card outlines (default for *) - border-strong
var(--border-strong)border-border-strongL #d3d7de · D #323946Hover borders, checkbox/radio outlines, dashed nodes
Text
- fg
var(--fg)text-fgL #111318 · D #f5f7faPrimary text, titles, valuesL18.6AAAD17.2AAA - fg-2
var(--fg-2)text-fg-2L #6b7280 · D #8b95a5Secondary text, descriptions, inactive navL4.8AAD6.1AA - fg-3
var(--fg-3)text-fg-3L #6f7785 · D #78818fMuted meta, placeholders, overlines, icons (AA)L4.5AAD4.7AA
Brand
- accent
var(--accent)bg-accent · bg-accent/10 · border-accent/30L #7c5cff · D #7c5cffPrimary actions, progress, current node, focus ring - accent-hover
var(--accent-hover)hover:bg-accent-hoverL #6b4bf0 · D #8e72ffPrimary button hover - accent-fg
var(--accent-fg)text-accent-fgL #ffffff · D #ffffffText/icons on solid accent - accent-solid
var(--accent-solid)bg-accent-solid · hover:bg-accent-solid-hoverL #7453fa · D #7453faFilled surfaces with white text: primary buttons, solid badges (AA) - accent-text
var(--accent-text)text-accent-textL #5f3df0 · D #a591ffAccent text & icons on surfaces (never text-accent)L6.2AAD7.1AAA
Status
- success
var(--success)text-success · bg-success/10L #1a7f37 · D #3fb950Completed, approved, positive deltasL5.1AAD7.3AAA - warning
var(--warning)text-warning · bg-warning/10L #955800 · D #e0a33aDue soon, needs changes, streak, rescheduledL5.7AAD8.3AAA - danger
var(--danger)text-danger · bg-danger/10L #cf222e · D #f85149Errors, overdue, cancelled, live indicatorL5.4AAD5.5AA - info
var(--info)text-info · bg-info/10L #0969da · D #4c9affIn progress, informational, streak freezeL5.2AAD6.5AA - perfect
var(--perfect)text-perfect · bg-perfect/10L #8f6300 · D #f2c94cPerfect score, achievements, rank examsL5.3AAD11.6AAA - success-solid
var(--success-solid)bg-success-solidL #1a7f37 · D #238636Filled success buttons with white text (AA) - danger-solid
var(--danger-solid)bg-danger-solidL #cf222e · D #da3633Filled danger buttons with white text (AA)
Status tints
Tinted fills for badges, callouts and icon tiles: bg /10, border /25 (perfect /30).
Side by side
The same composition rendered in both themes (tokens re-scoped inline).
Light
420 / 500 XP · 2 days left
Muted meta text (fg-3)
Dark
420 / 500 XP · 2 days left
Muted meta text (fg-3)
Typography
Geist Sans for UI, Geist Mono for code and ids. Base is 14/20 with cv11 and ss01 features. Weights: 400 body, 500 labels & nav, 600 titles — never bold (700) in UI. Add tabular to every number that sits in a column or counts.
Scale
As used in the product. Prefer the component (PageHeader, SectionHeader, CardTitle) over raw classes.
- Display28/36 → 34/40 · 600Countdown, celebration numbers02 : 14 : 09
text-[28px] leading-9 font-semibold tracking-tight tabular sm:text-[34px] sm:leading-10 - Page title22/28 → 24/32 · 600 · −0.02emPageHeader <h1>Programming Architecture
text-[22px] leading-7 font-semibold tracking-[-0.02em] sm:text-2xl sm:leading-8 - Hero title20/28 → 22/28 · 600NextLessonCard, Continue learningClasses & Objects
text-xl leading-7 font-semibold tracking-tight sm:text-[22px] - Stat value24/28 · 600 · tabularStatCard value11,140
tabular text-2xl leading-7 font-semibold tracking-tight - Dialog title16/24 · 600DialogTitle, SheetTitleReschedule lesson
text-base font-semibold tracking-tight - Section title15/20 · 600SectionHeader <h2>Upcoming lessons
text-[15px] font-semibold tracking-tight - Card title14/20 · 600CardTitleRank progress
text-sm font-semibold tracking-tight - Body14/20 · 400Default body (set on <body>)Implement Idle, Patrol and Chase states.
text-sm - Body small13/20 · 400–500Lists, table cells, nav items, descriptionsChapter 4 · Lesson 18 of 42
text-[13px] - Meta12/16 · 400–500Timestamps, hints, field errorsUpdated 2h ago
text-xs text-fg-2 - Overline11/16 · 500 · uppercaseGroup labels, menu labelsLearning
text-[11px] font-medium tracking-wide text-fg-3 uppercase - Micro10/14 · 500Chart axes, countdown unitshours · min · sec
text-[10px] font-medium tracking-wider text-fg-3 uppercase - Lesson prose16/28 · 400.prose-lesson reading surfacesA class is a blueprint for objects.
prose-lesson - Code13/22 · Geist MonoCodeBlock, inline code, idsGetComponent<Rigidbody>()
font-mono text-[13px] leading-[22px]
Families
font-sans · var(--font-geist-sans)font-mono · var(--font-geist-mono)Numbers
Use .tabular so digits align and counters don't jitter.
Scale
0.52pxIcon nudges, chart gaps14pxBadge gap, tight stacks1.56pxLabel → control, meta stacks28pxButton icon gap, inline groups2.510pxNav item padding, list icon gap312pxRow gap in lists, card header gap416pxGrid gap (gap-4), mobile page padding520pxCard padding (px-5), hero gaps624pxSection spacing (mt-6), sm page padding832pxLarge section spacing (mt-8), lg page padding1040pxDocs section rhythm1456pxEmpty state vertical padding
Rhythm rules
- Card padding
px-5 (20px) · header pt-4 pb-3 · hero p-5 sm:p-6 - Grid gap
gap-4 (16px) between cards and columns - Section spacing
mt-4 between dashboard rows · mt-6 / mt-8 between sections - Page padding
px-4 → sm:px-6 → lg:px-8 · top pt-5 → sm:pt-7 - Page bottom
pb-24 on mobile (clears 64px bottom nav) → lg:pb-12 - Stacks
label → control gap-1.5 · list rows py-3 · meta stacks gap-0.5
Borders
1px hairlines carry most of the structure — especially in dark mode where shadows are off. `*` defaults to border-border.
Shadows
Three elevations only. Dark theme: cards are flat (border only), popovers get a deep shadow, glow is reserved for the one thing that needs attention now.
shadow-card
Resting cards. Dark theme relies on borders only.
L 0 1px 2px rgb(16 24 40 / 0.04)D noneshadow-pop
Popovers, menus, dialogs, sheets, toasts, tooltips
L 0 12px 32px -8px rgb(16 24 40 / 0.16), 0 2px 6px rgb(16 24 40 / 0.06)D 0 16px 40px -8px rgb(0 0 0 / 0.6), 0 0 0 1px rgb(255 255 255 / 0.02)shadow-glow
Current focus only: live / starting-soon lesson, current roadmap node
L 0 0 0 4px rgb(124 92 255 / 0.14)D 0 0 0 4px rgb(124 92 255 / 0.18), 0 0 24px -4px rgb(124 92 255 / 0.45)Motion
Fast and quiet: 150–250ms for UI, one expo-out curve cubic-bezier(0.16, 1, 0.3, 1) for things that enter. Only celebrations use longer motion. prefers-reduced-motion collapses every animation to 0.01ms globally.
src/app/globals.cssDurations & utilities
| Preview | Name | Utility | Duration | Usage |
|---|---|---|---|---|
| transition (hover) | transition-colors duration-150 | 150ms | Hover / focus colour changes on every control | |
| fade-in | animate-fade-in | 160ms | Overlays, tooltips | |
| scale-in | animate-scale-in | 160ms | Popovers, menus, dialogs | |
| slide-in-right | animate-slide-in-right | 240ms | Right sheet (create / edit) | |
| slide-in-left | animate-slide-in-left | 240ms | Mobile navigation drawer | |
| slide-up | animate-slide-up | 240ms | Bottom sheet | |
| rise | animate-rise | 600ms | Celebration screens, staggered hero content | |
| progress | transition-[width] duration-700 ease-out | 700ms | Progress / ProgressRing fill | |
| shimmer | .skeleton / animate-shimmer | 1.6s loop | Skeleton loading | |
| pulse-soft | animate-pulse-soft | 2s loop | Waiting / syncing indicators |
Iconography
lucide-react v1, stroke 2, inheriting currentColor. Components size icons for you ([&_svg]:size-4) — pass the icon as a child. Completed checks use stroke 2.5–3. lucide v1 has no brand marks: use GithubIcon from @/components/pathly/icons; Google Meet is represented by Video.
lucide-reactCommon icons
Sizes
Rules
- Icons in text use
text-fg-2/text-fg-3; the label carries meaning. - Icon-only buttons need
aria-labeland aTooltip. - Status = colour + icon + label, never colour alone.
- No filled/duotone icon sets, no emoji.
Layout
Fixed 232px sidebar and sticky 60px header on desktop; below 1024px the sidebar becomes a left drawer and a 64px bottom nav appears. Every page renders inside <Page>.
import { Page, PageHeader, SectionHeader } from "@/components/pathly/layout"Desktop frame · ≥ 1024px
Mobile frame · < 1024px
Bottom nav 64px · 5 items · safe-area padded
Breakpoints
| Name | Min width | What changes |
|---|---|---|
base | 0 | Single column, bottom nav, full-width sheets. Design at 375px. |
sm | 640px | Two-column forms, inline actions, page padding 24px |
md | 768px | Search field in header, 2-column card grids |
lg | 1024px | Sidebar appears (232px), bottom nav hides, 12-column dashboards |
xl | 1280px | Wider dashboard splits, 3-up grids |
2xl | 1536px | Content stays capped at 1320px |
Page widths
width="default"Dashboards, lists, calendarsmax-w-[1320px]width="narrow"Details, settings, formsmax-w-[960px]width="prose"Lessons, articlesmax-w-[760px]width="full"Builders, full-bleed canvases—
Layers
z-20Sticky app headerz-30Sidebar, mobile bottom navz-40Docs headerz-50Overlays, dialogs, sheets, popovers, menusz-[60]Tooltips
Dashboard grid recipe
12 columns from lg. Pair spans that add up to 12; collapse to one column below md.
Grid gap is always gap-4 (16px). Rows are separated with mt-4. Wrap the whole row in a card list below md.
Components
shadcn-style wrappers around Radix primitives (radix-ui package), cmdk and sonner. Import from src/components/ui; extend with className (merged via tailwind-merge) rather than forking.
Input & Textarea
36px high, 8px radius, accent border + 3px ring on focus. Set aria-invalid for the error style and pass the message to Field. Use leading / trailing slots for icons, Kbd hints or a reveal button.
import { Input, Textarea } from "@/components/ui/input"@/components/ui/label → Field, LabelAs it appears on certificates.
Enter a valid email address
Managed by your teacher.
Falls back to your default room.
Supports **bold** and `code`.
Select
Radix Select with popper positioning. Use for 4+ options; fewer options → RadioGroup or ToggleGroup.
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@/components/ui/select"Grouped with labels and separators
Checkbox · Switch · Radio
18px controls with a label that is always clickable. Switch = settings that apply immediately; Checkbox = selections submitted with a form.
import { Checkbox, Switch, RadioGroup, RadioGroupItem } from "@/components/ui/checkbox"Checkbox
Switch
md 36×20 · sm 28×16
Radio group
Tabs
underline = page-level sections (scrolls horizontally on mobile); segmented = compact switcher inside cards and toolbars. Optional count pill.
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"variant="underline"
variant="segmented"
ToggleGroup & Slider
ToggleGroup mirrors the segmented tabs look for view options that are not navigation. Slider for bounded numeric input.
import { ToggleGroup, ToggleGroupItem, Slider } from "@/components/ui/misc"Badge
Compact status/meta label. sm (20px) inside rows and cards, md (24px) in headers. Domain badges (status, difficulty) are built on this — prefer them.
import { Badge } from "@/components/ui/badge"| Tone | md | sm | dot | icon |
|---|---|---|---|---|
neutral | Label | Label | Label | +40 XP |
accent | Label | Label | Label | +40 XP |
success | Label | Label | Label | +40 XP |
warning | Label | Label | Label | +40 XP |
danger | Label | Label | Label | +40 XP |
info | Label | Label | Label | +40 XP |
perfect | Label | Label | Label | +40 XP |
outline | Label | Label | Label | +40 XP |
solid | Label | Label | Label | +40 XP |
Card
rounded-xl · 1px border · bg-surface · shadow-card (none in dark). Interactive cards lift the border and tint the background on hover — wrap them in a Link, never nest buttons inside a clickable card.
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card"Static card
Header · content · footer
interactive adds hover border + tint. Hover me.Goal 500 XP
Table
13px cells, 36px header, hairline rows with hover tint; data-state="selected" tints accent. Wraps in horizontal scroll — on mobile prefer a card list for more than 3 columns.
import { Table, THead, TBody, TR, TH, TD } from "@/components/ui/table"| Student | Assignment | Status | XP | ||
|---|---|---|---|---|---|
| AMAlex Morgan | Enemy AI — FSM | Under review | 2h ago | +120 | |
| MCMaria Chen | Inventory System | Needs changes | Yesterday | +100 | |
| SRSofia Rossi | LINQ Practice | Approved | Sep 16 | +40 |
Dialog
Centered modal, sizes sm 384 · md 512 · lg 672 · xl 896. Header / Body (scrolls) / Footer (buttons right, stacked on mobile). Destructive confirmations put the danger button last.
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody, DialogFooter } from "@/components/ui/dialog"Sheet / Drawer
right = create/edit flows (full width on mobile, 520px from sm) · left = mobile navigation (280px) · bottom = mobile pickers and quick actions.
import { Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetBody, SheetFooter } from "@/components/ui/sheet"Popover · Tooltip · HoverCard
Popover for interactive content (filters, Meet link details). Tooltip for short labels on icon buttons, optionally with a shortcut. HoverCard for previews on hover (student, article).
import { Popover, PopoverTrigger, PopoverContent, Tooltip, HoverCard } from "@/components/ui/popover"Toast
sonner, themed in providers.tsx: bottom-right on desktop, above the bottom nav on mobile. Confirm completed actions; offer Undo instead of a confirm dialog for reversible ones. Errors that block the page belong in ErrorState, not a toast.
import { toast } from "sonner"Progress & ProgressRing
Linear bar for XP, course and goals (accent by default; skill scores use skillTone). Ring for compact percentages in cards. Both animate width/offset over 700ms.
@/components/ui/misc → Progress · @/components/pathly/progression → ProgressRingProgress · tones & sizes
accentsuccesswarningdangerinfoneutralxssmmdlgProgressRing
Avatar
Initials on a hue-tinted gradient (User.avatarHue), falls back gracefully without images. Title attribute exposes the full name.
import { Avatar } from "@/components/ui/avatar"Skeleton
Shimmer placeholder that mirrors the final layout (same heights and radii) so content doesn't jump. Presets live in pathly/states — see Patterns → States.
import { Skeleton } from "@/components/ui/misc"Text
List row
Profile
Kbd & Command palette
The palette opens with ⌘K / Ctrl K anywhere in the app shell (useCommandPalette() from @/components/shell/command-palette). Navigation shortcuts use G-sequences (G H, G R…). Below: the cmdk primitives rendered inline.
@/components/ui/misc → Kbd · @/components/ui/commandKbd
Command (inline)
Pathly patterns
Domain components in src/components/pathly, src/components/charts and src/components/shell. They encode product rules (node shapes, rank tiers, scheduling states) — reuse them instead of rebuilding the visuals.
Roadmap nodes
NodeGlyph: shape encodes the type (circle = lesson / practice / quiz / challenge, hexagon = project, diamond = checkpoint, shield = rank exam, seal = milestone), colour + icon encode the state. Completed nodes swap the type icon for a check (showTypeIconWhenDone keeps it). current adds the accent glow — only one per roadmap.
import { NodeGlyph } from "@/components/pathly/roadmap-node"Type × state matrix
8 types × 5 states + current
| Type | locked | available | in_progress | completed | perfect | current |
|---|---|---|---|---|---|---|
Lessonlesson · circle | ||||||
Practicepractice · circle | ||||||
Quizquiz · circle | ||||||
Challengechallenge · circle | ||||||
Projectproject · hexagon | ||||||
Checkpointcheckpoint · diamond | ||||||
Rank Examrank_exam · shield | ||||||
Milestonemilestone · star |
Roadmap strip
Connector: bg-accent/60 up to the current node, bg-border after it.
Sizes & state badges
Ranks
RankEmblem is monochrome and geometric. Tier (1–4) = number of nested hexagon rings (tier 4 gets a filled core); order within the tier = one or two pips. muted for locked / next ranks, glow only on celebration screens.
import { RankEmblem } from "@/components/pathly/progression"Sizes & variants
Level & XP
XP needed for level L → L+1 is 400 + 100 × (L − 1) (levelFromXp in @/lib/progression). XP values always use tabular and the accent text colour; negative adjustments are danger.
import { LevelBadge, XpBar, XpAmount } from "@/components/pathly/progression"LevelBadge
XpBar
XpAmount
Streak
Warning-toned flame for an active streak, neutral when broken. StreakDots shows the last 14 days; snowflake = day covered by a Streak Freeze (info).
import { StreakChip, StreakDots } from "@/components/pathly/progression"Progression widgets
Composite cards shared by the student Dashboard, Progress and the teacher's Student details. Pass data from query hooks; they own no fetching.
import { RankProgressCard, WeeklyGoalCard, SkillRow } from "@/components/pathly/widgets"Rank progress
4/5 requirements- Reach Level 12Level 12
- Complete Unity FoundationsChapter 3
- Complete 3 Projects3 / 3
- Checkpoint score 80%+84%
- Pass Developer II ExamNot attempted
SkillRow + SkillMeter
Tone from skillTone(score): ≥75 success · ≥60 accent · ≥45 warning · else danger
Next lesson card — 7 scheduling states
The dashboard hero. The display state is derived client-side by lessonDisplayState(lesson, now) in @/lib/progression: server cancelled wins, then time decides. Only starting_soon and live get the accent border + glow and a primary Meet button. Use stateOverride for previews and tests.
import { NextLessonCard } from "@/components/pathly/lesson-schedule"Status badges
One badge per domain enum so labels, tones and icons stay consistent. The maps (SUBMISSION_STATUS, NODE_STATE, PROJECT_STATUS, LESSON_STATE) are exported for custom renderings.
import { SubmissionStatusBadge, NodeStateBadge, ProjectStatusBadge, LessonStateBadge, DifficultyBadge } from "@/components/pathly/status"SubmissionStatusBadgeNodeStateBadgeProjectStatusBadgeLessonStateBadgeDifficultyBadgeEmpty · Error · Loading
Every data-driven region handles all three. QueryBoundary wires them to a TanStack query: skeleton while pending, ErrorState with Retry on error, empty when isEmpty(data). Preview in the app via account menu → Simulate (slow / error / empty).
import { QueryBoundary, EmptyState, ErrorState, CardSkeleton, ListSkeleton, GridSkeleton } from "@/components/pathly/states"EmptyState
Icon + title + one-line reason + optional next step
You're all caught up
No homework right now. New assignments appear here as soon as your teacher publishes them.
ErrorState
role=alert · Retry shows a loading state
Failed to load assignments
We couldn't load this content. Check your connection and try again.
compact
Inside cards and popovers
No notifications yet
We'll let you know when something happens.
Couldn't load
We couldn't load this content. Check your connection and try again.
Skeleton presets
GridSkeleton
count · className · itemClassName
Notification center
Header bell → popover (All / Unread tabs, grouped Today / Yesterday / Earlier, 12 items max) → full page. Unread = semibold title + accent dot. Lesson reminders inside the join window get an inline Join button; reschedules show old → new time. Every type maps to an icon + tone in NOTIFICATION_META; emails mirror the same types.
import { NotificationBell, NotificationList, NotificationItem } from "@/components/shell/notification-center"Popover (static preview)
Real component needs the query client + router
Types → icon + tone
@/components/pathly/icons → NOTIFICATION_METAlesson_createdaccentlesson_rescheduledwarninglesson_cancelleddangerlesson_24h_reminderaccentlesson_1h_reminderaccentlesson_15m_reminderaccentassignment_createdinfoassignment_due_soonwarningsubmission_reviewedsuccesssubmission_receivedinfoteacher_commentneutralachievement_unlockedperfectxp_awardedaccentlevel_upaccentrank_upperfect
Calendar event chips
One chip per CalendarEventKind using CALENDAR_EVENT_META (label, icon, tone). Tone lives on the left border, tint and icon; the title stays text-fg for legibility. Cancelled = dashed + strikethrough; rescheduled = warning clock.
import { CALENDAR_EVENT_META } from "@/components/pathly/icons"Month chip
States
19:00Unity Physics18:00Unity PhysicsWeek block
Legend / agenda dot
- Lesson
accent - Homework deadline
warning - Project deadline
info - Checkpoint
success - Rank exam
perfect
Code
CodeBlock lazy-loads a fine-grained Shiki bundle (C#, TypeScript, JSON, Bash, Python) with GitHub light/dark themes, and falls back to plain monospace until ready. Filename, language label, copy, line numbers and highlighted lines. RichText renders **bold** and `code` in plain strings (feedback, hints).
import { CodeBlock, CopyButton, RichText } from "@/components/pathly/code-block"public class EnemyController : MonoBehaviour
{
[SerializeField] private float chaseRange = 6f;
private IEnemyState _state;
private void Update()
{
_state = _state.Tick(this);
}
}
RichText
Great work! Consider caching GetComponent<Rigidbody>() in Awake instead of calling it every frame.
CopyButton
Charts
Single-series, token-coloured, hover-first. Bars ≤ 24px with 4px rounded data-end, 2px lines with a 10% area wash, hairline recessive grid, end dot with a surface ring. Text never wears the series colour; every mark has a tooltip and an accessible label.
import { ColumnChart, LineChart, ActivityHeatmap, Sparkline } from "@/components/charts"ColumnChart
Weekly XP · highlightIndex = current day
LineChart
Skill score over 13 weeks · yMax 100
| W1 | 42% |
|---|---|
| W2 | 45% |
| W3 | 47% |
| W4 | 51% |
| W5 | 52% |
| W6 | 56% |
| W7 | 58% |
| W8 | 61% |
| W9 | 63% |
| W10 | 66% |
| W11 | 70% |
| W12 | 74% |
| W13 | 78% |
ActivityHeatmap
16 weeks · GitHub-style, 5 steps of accent
Sparkline
Muted line + accent end dot, inside stat cards
This week
Page scaffolding
PageHeader (breadcrumbs, eyebrow, title, description, actions — actions wrap under the title on mobile), SectionHeader (15px title, optional icon, count and action), StatCard, MetaRow for detail sidebars.
import { Page, PageHeader, SectionHeader, StatCard, MetaRow, Breadcrumbs } from "@/components/pathly/layout"PageHeader
Enemy AI — Finite State Machine
Implement Idle, Patrol and Chase states for the enemy using a simple finite state machine.
SectionHeader
Upcoming lessons4
Next 14 days
MetaRow · Breadcrumbs
Details
Due soonStatCard row
1,240 / 1,500 XP
Longest 19
Icon tiles & domain icons
IconTile = tinted rounded square (sm 28 · md 36 · lg 44) used in lists, notifications and empty states. Domain icon maps keep the same concept on the same glyph everywhere.
import { IconTile, TONE_CLASSES, NODE_META, SKILL_ICONS, ACHIEVEMENT_ICONS } from "@/components/pathly/icons"IconTile · tones × sizes
smmdlgaccent · warning · info · success · danger · perfect · neutral
Skills · node types
csharpunitydebuggingarchitecturealgorithmsuilessonpracticequizchallengeprojectcheckpointrank_exammilestoneAchievement icons
ACHIEVEMENT_ICONS — keyed by Achievement.icon
footprintsbugeye-offtargetflamehammerbookzaptrophybrainrocketshieldclockstarpuzzlegitsunriselayers