Oct 28, 2026
Mat Bloom- The package is now called @itc/avro-arrow-design-system. Product repos use this name in their install, import and Claude instructions.
AVRO Product Design System
Click anywhere to enter
Created by
The single source of truth for how ITC products look and behave.
ITC ships fast with AI-assisted development. Speed without a shared visual language produced UI that is inconsistent across apps, cluttered and visibly "AI generic". This system makes the default output good: humans and AI tools build from the same tokens, specs and rules, so consistency is the path of least resistance.
Ambition: ITC products feel calm, warm and considered: well-spaced, plainly worded and recognisably one family across every ITC product, web and mobile. Clearly better than CBORD/Illumia, TouchNet and legacy campus-card software, and friendlier than typical admin tools.
Quiet by default, loud on purpose.
Neutral surfaces; colour reserved for action, selection and status.
Comfortable by default, compact for data.
Generous space around content (32px gutters, 24px cards, 48px between sections); tables and toolbars use the compact sizes.
Hierarchy from size and space, not boxes.
Big clear titles and white space do the grouping. Box only what needs it; side rails are unboxed lists.
Same anatomy every time.
One way to do a thing: one button per role, one table, one settings row, one modal.
Say what matters.
Pages open with a plain sentence that summarises and links to what needs attention; every setting says what it does.
Accessible is non-negotiable.
WCAG 2.2 AA (supports ITC's VPAT/HECVAT obligations).
Same family, different rooms.
All apps share tokens; each tunes density to its users.
One shared set of rules and ready-made parts that every ITC product is built from. Its biggest job is not colour or fonts: it makes every common task work the same way everywhere, makes our products accessible by default, and gives them the finish of a purpose-built platform.
It's the agreed way we build screens. Decisions like "where does the main button go?", "how do we confirm a save?" or "how do we warn someone before they delete something?" are made once, written down, and turned into ready-made parts that every developer and AI tool uses. Nobody re-decides them screen by screen, so users get the same experience wherever they are in our products.
Today, AI-assisted builds of AVRO solve these moments differently from screen to screen. The design system sets one pattern for each, so users learn it once.
| When someone… | It always works like this |
|---|---|
| Looks for the main action | One primary button per page, always top-right in the page header, named for what it does ("Add client", not "Submit"). In dialogs it sits bottom-right, with Cancel to its left. |
| Completes an action | A short confirmation toast appears bottom-right saying exactly what happened ("Jordan Lee added"), with Undo where possible. No pop-up boxes to dismiss. |
| Needs to know about a problem | An alert at the top of the section says what's wrong and how to fix it, with a link to do so ("12 photos are waiting for approval. Review photos"). |
| Makes a mistake in a form | The message appears under the field, says what to enter and gives an example. Labels are always visible, never placeholder text that disappears. |
| Deletes or archives something | A confirmation dialog names what and how many ("Archive 2 clients?"), explains the consequence, and the button repeats it. The safe choice is the default. |
| Acts on a row in a list | One "more" menu per row, with destructive actions last, instead of loose delete icons on every row. |
| Searches or filters a list | The same filter bar on every list: search, filters, applied filters shown as chips with "Clear all", then the same table, pagination and bulk-selection bar. |
| Finds nothing there | An empty state says what belongs here and how to start, and tells "nothing yet" apart from "no results" (with Clear filters). |
| Changes a setting | Every setting says what it does in plain words. Switches take effect immediately and confirm with a toast; no hunting for a Save button. |
| Works through a multi-step task | A "Step 2 of 4" indicator, with Back and Continue always in the same place. |
| Reads a status | The same status words and colours for the same thing everywhere (Active, Suspended, Card lost), always with text, never colour alone. |
| Edits a record | One rule for when editing happens in a dialog, inline or in a side panel, so the same kind of edit never opens three different ways. |
| Leaves with unsaved changes | One standard "You have unsaved changes" save bar and leave-confirmation, so work is never lost silently. |
Customers judge software before they use it. A platform that looks bespoke, calm and consistent reads as mature, secure and worth its price; one that looks assembled from defaults reads as cheap, however good the engineering. The design system gives every ITC product that purpose-built finish for free, so it supports sales demos, renewals and pricing, and sets us apart from CBORD, TouchNet and legacy campus-card tools. The team never has to think about it: the look comes with the parts.
More and more of our UI is written with AI tools like Claude Code. An AI tool is fast, but it only knows what it is told. With no ITC rules, every session invents its own answers to the questions above.
| Today | With the design system |
|---|---|
| Every screen decides for itself where buttons go, how errors show and how deletes are confirmed. | Decided once. Each pattern above is written down as a spec with ready-made markup. |
| Claude Code doesn't know our conventions, so it guesses, differently each time. | Claude Code reads our rules first. Each product repo tells it to follow the system; before building a table or a dialog it reads that spec and uses our part. |
| Products drift apart. AVRO, GoPrint and the mobile app solve the same task differently. | One shared package installed in every product, so the same task works the same everywhere. |
| Inconsistencies are found late, by eye, screen by screen. | Rules are checked automatically, and anything the system doesn't cover yet is flagged in the code instead of invented. |
| Outcome | What it means |
|---|---|
| Predictable for users | The same task works the same way in every screen and product, so less training, fewer support calls and fewer mistakes. |
| Accessible by default | Keyboard, screen-reader and contrast requirements come with each part, not as a retrofit before an audit. |
| Faster delivery | Developers and AI tools assemble screens from finished parts, with no design debates about where a button goes. |
| One product family | AVRO, the mobile app, GoPrint and future products behave and read as one company. |
| Perceived value | A bespoke, consistent finish that supports demos, renewals and pricing. |
| One source, kept current | Every rule and part lives in one place. Improve it once, and every product gets it with a version update. |
A pattern, part or rule is updated in the design system.
Specs, the gallery and the AVRO One preview regenerate, and rule breaks fail the build.
The design owner reviews it; it ships with a version number and changelog.
Teams bump the version. AI tools follow the new rules on their next task.
When a screen needs something the system doesn't cover, developers and AI tools flag it in the code (DS-GAP) instead of inventing it. The design owner adds the pattern, and it appears in the gallery and preview automatically.
See it: start with Preview → AVRO One.
Being defined next: the editing pattern (dialog, inline or side panel) and the unsaved-changes standard.
Open decisions: final brand colour sign-off, how Angular apps load the icons, and "client" vs "patron" in product copy.
Three groups use the design system every day. AI agents come first, because they write most of our UI and follow the system most literally.
Built for AI tools first
Everything in this gallery also exists as plain Markdown files, written and structured for AI tools like Claude Code to read. Every product repo points its AI at them, so the rules travel with the code.
AI-RULES.mdShort, strict rules. Loaded into every product repo's Claude instructions, so every session starts with them.components/*.mdOne spec per component: when to use it, the markup to copy, its classes, behaviour, and do's and don'ts.DESIGN.mdPrinciples and foundations: the why behind every rule.docs/*.mdSpacing, icons, tone of voice, brand, and how we build on Tailwind.CHANGELOG.mdWhat changed, in plain English, so agents and people see what's new.Two lines in the repo's CLAUDE.md:
All UI MUST follow @node_modules/@itc/avro-arrow-design-system/AI-RULES.md Before building any UI, read the matching spec in node_modules/@itc/avro-arrow-design-system/components/.
The first and most important audience. Most of our UI is now written with AI tools like Claude Code, and an AI tool only knows what it is told. The design system is what it is told.
AI-RULES.md, so every session starts with the same short, strict rules.components/ and copies its markup, so it doesn't guess.DS-GAP or DS-QUESTION note for a person to decide, instead of making something up.For the people who build and shape the products.
btn, table, modal…), with markup to copy and accessibility built in.For the people who check that it works.
Every tool in this system was chosen on purpose. Each choice says why, what else we considered, and what it gives us. Anything not listed here needs a design-system change request.
Why we chose it
One theme file becomes both the values and the class names, so there is no second place for styles to drift. AI tools like Claude Code write Tailwind fluently, so pairing it with our tokens makes their output consistent.
What we considered
Angular Material (looks like Google and is hard to make our own), Bootstrap (generic, heavy overrides), and SCSS or CSS Modules (every team and AI session invents its own names).
What it gives us
AI-written screens that match ours, small bundles, and off-system values that stand out in review. The trade-off, long class lists, is solved by our ready-made component classes.
Why we chose it
A large, MIT-licensed set with a true filled version of every icon, used exactly as Phosphor ships it.
What we considered
Lucide (outline only, and faking filled icons broke several), Font Awesome (heavier, needs attribution) and Heroicons (too few icons for campus meanings).
What it gives us
A friendly, bespoke look, one icon per meaning, and nothing custom to maintain.
Why we chose it
One friendly, geometric typeface for everything, including numbers and money.
What we considered
Pairing Inter with Outfit, and plain system fonts.
What it gives us
A warmer, more recognisable product and one font to load. The trade-off: Outfit has no fixed-width digits, so numbers in columns are right-aligned.
Why we chose it
Proven keyboard, focus and screen-reader behaviour for menus, dialogs, tabs and lists, maintained by the Angular team.
What we considered
Writing that behaviour ourselves, or adopting a full component library with its own look.
What it gives us
Accessibility we don't have to reinvent, while the styling stays ours.
Why we chose it
AI tools read plain files in the repo directly, and the build keeps them in step with the code.
What we considered
Wiki pages and Figma-only specs, which AI tools can't read reliably or which drift from the code.
What it gives us
AI output that follows our rules from the first prompt, and docs that can't fall out of date.
Also used: @tailwindcss/postcss to plug Tailwind into each app's build, and Google Fonts to load Outfit (self-hosting is an open decision).
Uses 163 of 166 component classes.
ITC Systems is the master brand; each product has its own logo in the same family.
Full · on light
Full · on dark
Mark only
Full · on light
Full · on dark
Mark only
Full · on light
Full · on dark
Mark only
Full · on light
Full · on dark
Mark only
Neutral ink carries ~90% of every screen. AVRO Indigo (matched to the ITC mobile app) is for action, selection, links and focus; its -50 tint carries soft colour. Brand values are placeholders pending marketing sign-off.
50
#f3f2fd
100
#e8e6fb
200
#d3cff7
300
#b3acf0
400
#8c80e6
500
#6c5dd9
600
#5443c6
700
#4636a8
800
#3a2e88
900
#30296b
950
#1f1a47
0
#ffffff
25
#fbfbfd
50
#f6f6f9
100
#eeeef3
150
#e5e5ec
200
#d8d8e1
300
#bbbbc8
400
#8e8ea0
500
#6b6b7e
600
#525266
700
#3d3d4f
800
#2a2a39
900
#1b1b27
950
#0f0f17
50
#ecfdf7
100
#d1fae9
500
#10b981
600
#059669
700
#047857
surface
canvas
subtle
line
line-strong
fg
fg-muted
fg-subtle
primary
primary-hover
focus
One typeface, Outfit, for 100% of the UI. Body 15px, tables 14px, weights 400/500/600, sentence case.
The only typeface · font-sans
Outfit $1,280.50 · 214432 · Oct 24, 2026
Headings, body, tables, labels, badges, numbers and money. Hierarchy comes from size, weight and colour, never a second typeface. Outfit has no tabular figures, so right-align numbers in columns.
Outfit weights · Google Fonts specimen
Aa Bb 123
400 regular
Aa Bb 123
500 medium · buttons, links, nav
Aa Bb 123
600 semibold · headings
ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789 & ? ! @ $ %
Pairing: headings + body
Everyone with an account at Seneca Polytechnic. Balances refresh every 15 minutes from AVRO.
Lost cards are suspended immediately; a replacement can be issued from the patron record.
Manage card policiesUI and listings, all Outfit
| Jordan Lee ID 300124 | $42.50 |
| Priya Shah ID 300981 | $118.00 |
Phosphor, Fill weight, used as-is. Two sizes, one meaning per icon. Full rules: docs/iconography.md.
Weight
Fill for everything. Bold only for simple marks (plus, x, check, dots, carets, arrows). Original SVGs, never edited.
Sizes
16 inline with text · 20 nav, icon buttons, tiles, alerts.
Colour
currentColor: same as the text beside it · primary when active · status colours for status only.
No supported icon matches. Flag the gap with <!-- DS-GAP: icon for … -->. Don't pick your own.
Do
Icon + text for actions, 16px, gap-2.
Don't
Use Regular, Light, Thin or Duotone, or edit an icon. Fill (Bold for simple marks) only.
Don't
Use an icon that isn't in the list (it may not fill cleanly), or colour icons for decoration.
Don't
Icon-only primary actions. Icon-only is for secondary actions, with aria-label + tooltip.
How much space goes where: page frame, page structure, inside containers, between elements. Generated from docs/spacing.md.
Proximity = relationship.
The more related two things are, the less space between them. Label→field is 6px; unrelated sections are 48px apart.
Space shrinks as you go in.
Page gutter 32 → card padding 24 → cell padding 16/14 → element gaps 8/6.
Only the scale.
4px grid, steps 1 1.5 2 2.5 3 4 5 6 8 10 12 16 (4–64px). No arbitrary values. Prefer gap-* on the parent over margins on children.
Tinted areas are spacing. Desktop layout, drawn at real size.
Clients 145 records
Title → description mt-2
Active clients
2,520
p-6Active clients
2,520
Active clients
2,520
Profiles
px-6 pt-6 pb-4| Where | Class | px |
|---|---|---|
| Sidebar width (on the canvas) | sidebar = w-64 px-3 pt-4 | 256; 12 sides |
| Nav item | nav-item = h-10 px-3 gap-3, items gap-0.5 | 40 tall, 12 sides, 2 between |
| Nav group label | nav-label = pt-6 pb-2 | 24 above, 8 below |
| Content sheet inset | app-main = py-2 pr-2 | 8 from the window edge |
| Top bar | topbar = h-16 px-8 gap-2 | 64 tall, 32 sides |
| Page gutter | app-content = px-8 py-8 | 32 |
| Content width | app-content-inner = max-w-content (settings max-w-settings, forms max-w-form) | max 1280 (880, 640) |
| Where | Class | px |
|---|---|---|
| Page header → first content | page-header = pb-8 | 32 |
| Title → description | page-description = mt-2 | 8 |
| Between sections (unrelated groups) | gap-12 | 48 |
| Between blocks in a section (alert → stats → table) | gap-8 | 32 |
| Between cards in a grid or row | gap-4 | 16 |
| Tightly related blocks (filter bar → table) | gap-3 | 12 |
| Tabs → tab content | tab-panel = pt-6 | 24 |
| Main column → side rail | gap-10 | 40 |
| Section header → its content | section-header = pb-4 | 16 |
| Container | Padding | px |
|---|---|---|
| Card body / stat card | card-body = p-6 | 24 |
| Card header | card-header = px-6 pt-6 pb-4 | 24 / 16 below |
| Card list rows, list rows | py-4 | 16 |
| Settings row | settings-row = px-6 py-5 | 24 / 20 |
| Table cells | px-4 py-3.5; header py-3; footer px-4 py-3 | 16 / 14 |
| Modal | header px-6 pt-6; body p-6 (fields gap-5); footer px-6 py-4 | 24; 24; 24 / 16 |
| Alert | p-4, icon→text gap-3 | 16; 12 |
| Dropdown menu | panel p-1.5, items px-2.5 | 6; 10 |
| Empty state | px-6 py-16 | 24 / 64 |
| Where | Class | px |
|---|---|---|
| Label → control → help text | gap-1.5 | 6 |
| Between form fields (both directions) | gap-4 (modals gap-5) | 16 (20) |
| Button groups, filter controls | gap-2 | 8 |
| Filter chips | gap-1.5 | 6 |
| Icon → text | gap-2 (gap-3 in nav and lists) | 8 (12) |
| Tabs | gap-8 | 32 |
| Stat label → number → delta | mt-3, mt-1 | 12, 4 |
Touch-first, single column: gutters px-4 on phones, px-6 from sm; cards p-5; blocks gap-6; stacked full-width buttons gap-2; controls h-12 (btn-lg, input-lg).
gap-*.space-y-* across mixed content. It breaks when items are hidden.btn-sm, icon-btn-sm, the 14px table) instead.Soft, layered, low contrast. Flat beats shadowy.
Component
Frame for desktop apps: sidebar navigation, top bar and the content sheet. Spec: components/app-shell.md.
Default
aria-current="page" on the active link. Skip-to-content link first in the DOM.assets/logos/ in sidebar-header (wordmark until it lands; see docs/brand.md).Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Top of every page and every section: title, context, actions. Spec: components/page-header.md.
Page header
Everyone with a card or account at ITC Systems. 12 photos are waiting for approval.
Everyone with a card or account at ITC Systems. 12 photos are waiting for approval.
Section header
Changes in the last 24 hours.
Changes in the last 24 hours.
<h1> per page; sections are <h2>.page-description ("Welcome back, Patti. You have 12 cards ready to print.").Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Switch between views of the same object. Spec: components/tabs.md.
Default
FocusKeyManager).Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Groups related content on the sheet. Spec: components/card.md.
Card with list
Profiles set which plans, privileges and card design a client gets.
Profiles set which plans, privileges and card design a client gets.
Stat cards
stat-grid and can be links to the full list.Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Names an icon-only control on hover and focus. Spec: components/tooltip.md.
Icon button with tooltip
:focus-within). The trigger keeps its own aria-label.Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Label + control + help/error. All form controls use this wrapper. Spec: components/field.md.
Text input
Used for receipts and low-balance alerts.
Used for receipts and low-balance alerts.
Select
Error
214432 already belongs to Mat Bloom. Enter a different ID.
214432 already belongs to Mat Bloom. Enter a different ID.
Textarea
Disabled
aria-describedby; validate on blur, not keystroke.gap-4; in modals gap-5 (see docs/spacing.md).input select. Angular: CDK listbox / Angular Aria combobox for custom selects.input-lg (48px) only in touch-first apps (phones, tablets, kiosks).Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Select one or more independent options; bulk-select rows. Spec: components/checkbox.md.
Checked
Unchecked
Disabled
el.indeterminate = true + aria-checked="mixed".<fieldset><legend>.Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Pick exactly one option from a small set. Spec: components/radio.md.
Radio group
Choice cards
<fieldset> with a <legend> (visible or sr-only).sm up; the selected card is highlighted automatically.Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Turn a setting on or off immediately. Spec: components/switch.md.
On
Off
Disabled
role="switch" makes screen readers announce on/off.Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Search + filters + view controls above any list or table. Spec: components/filter-bar.md.
Default
gap-3.Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Primary way to show lists of records. Spec: components/data-table.md.
Default
| Profile | Status | Balance | Actions | ||
|---|---|---|---|---|---|
| Student | Active | $42.10 | |||
| Employee | Suspended | $3.20 |
| Profile | Status | Balance | Actions | ||
|---|---|---|---|---|---|
| Student | Active | $42.10 | |||
| Employee | Suspended | $3.20 |
Bulk action bar
first child of table-wrap while rows are selected
<table> semantics; sortable headers are buttons with aria-sort on the <th>.table-bulkbar at the top of the frame.@angular/cdk/table for virtualisation/large sets.num so place values line up.icon-btn icon-btn-sm in cell-actions, opening a Dropdown menu.Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Rows of related items outside a table: activity, things needing attention, quick actions, key figures. Spec: components/list.md.
Activity / needs attention
24 clients imported
Student intake · Bulk management
24 clients imported
Student intake · Bulk management
Key figures
<ul>/<li> for lists of things; links inside rows describe the destination.Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Status or category label. Not clickable. Spec: components/badge.md.
Neutral
Status
Brand and info
Count
count is for numbers in tabs and section headers, never for statuses.Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
A person: photo or initials. Spec: components/avatar.md.
Initials
Sizes
No photo
<img> an alt with the person's name.avatar (40px) by default, avatar-sm (32px) in the top bar and dense lists, avatar-lg (48px) in menus and profile headers.avatar-empty when a person has no photo yet.Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
A soft square that gives a row, card or setting a recognisable icon. Spec: components/icon-tile.md.
Neutral
Tones
Small
Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
One icon set, Phosphor Fill, used as-is. Spec: components/icon.md.
Fill
almost every icon
Bold
simple marks only
Status colours
aria-hidden="true". Icon-only buttons: aria-label + tooltip.currentColor. Never rely on an icon alone for status; pair it with text.docs/iconography.md, one meaning each.Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Inline, persistent message about a page or section. Spec: components/alert.md.
Warning with actions
12 photos are waiting for approval
Cards can't be printed for these clients until their photos are approved.
12 photos are waiting for approval
Cards can't be printed for these clients until their photos are approved.
Info
3 rows will be skipped
Their IDs already belong to archived clients.
3 rows will be skipped
Their IDs already belong to archived clients.
Success
Last close-out finished Oct 24, 2026
Last close-out finished Oct 24, 2026
Danger
Couldn't add Jordan Lee
Fix the highlighted field and try again.
Couldn't add Jordan Lee
Fix the highlighted field and try again.
role="alert" only for urgent errors; otherwise role="status".Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Brief confirmation after an action. Disappears on its own. Spec: components/toast.md.
Default
Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Focused task or confirmation that blocks the page. Spec: components/modal.md.
Default
Their cards stop working right away. Balances and history are kept.
Their cards stop working right away. Balances and history are kept.
Dialog: focus trap, Esc closes, focus restored to trigger. Initial focus on the least destructive action.modal (512px) for confirmations, modal-lg (672px) for short forms.Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
When a list or view has no data. Spec: components/empty-state.md.
Nothing yet
Import a CSV or add clients one at a time.
Import a CSV or add clients one at a time.
No results
Try a different search or clear the filters.
Try a different search or clear the filters.
Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Pages of on/off and pick-one options, one consequence per row. Spec: components/settings.md.
Default
What happens to balances and cards at the end of each term.
Carry balances forward
Unused meal and print balances move to next term instead of expiring.
Email clients before close-out
Clients with a balance get a reminder 7 days before their term ends.
What happens to balances and cards at the end of each term.
Carry balances forward
Unused meal and print balances move to next term instead of expiring.
Email clients before close-out
Clients with a balance get a reminder 7 days before their term ends.
Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
Component
Show progress through a multi-step task or a list of things to do. Spec: components/stepper.md.
Stepper
Checklist
Confirm term dates
Fall term ends Dec 18, 2026.
Review graduating clients
38 clients graduate this term.
Confirm term dates
Fall term ends Dec 18, 2026.
Review graduating clients
38 clients graduate this term.
aria-hidden.Read live from src/theme.css; the same table is generated into the spec.
classAVRO component classclassTailwind defaultclassAVRO tokenclassTailwind name, AVRO valueclassOne-off valueHover a class for what it does.| Class | Applies (Tailwind utilities) | Also |
|---|
One voice across every product, the website and email. Voice stays the same; tone shifts with the audience and the moment. Full guide: docs/tonality.md.
Plain words, short sentences, one idea each
Not: Jargon, vague, clever
Steady even when something breaks
Not: Alarmist, shouty, over-apologetic
Always point to the next step
Not: Blaming, dead-ends
Exact numbers, names and times
Not: “Some”, “soon”, “a few issues”
Human and respectful; greet people by name on home screens
Not: Jokes, emoji, exclamation marks, hype
| Audience | Tone | Sounds like |
|---|---|---|
| Admins · desktop tools | Efficient, precise | “Export report” · show IDs, counts, exact states |
| Parents & students · touch-first apps | Warm, reassuring, plain | “Your balance is $42.50” · explain outcomes, hide system detail |
| Walk-up users · kiosks | Direct, minimal | “Tap your card to release 3 jobs” · one step per screen |
| Moment | Do | Example |
|---|---|---|
| Success | Confirm briefly. No celebration. | Card issued to Jordan Lee. |
| Error | What happened + how to fix. Never blame. | Couldn’t save the patron. Check your connection and try again. |
| Warning | Consequence before the action. | Suspending this card blocks purchases and door access. |
| Destructive | Name the object and count; verb on the button. | Delete 3 cards? |
| Empty state | What this is + how to start. | No patrons yet. Import a CSV or add patrons one at a time. |
| Money | Reassure with facts. | $50.00 added. Your new balance is $92.50. |
| Page intro | One sentence: what this is, then what needs attention, linked. | Everyone with a card or account at Trent. 12 photos are waiting for approval. |
| Setting description | The outcome for people, not the mechanism. | Clients with a balance get a reminder 7 days before their term ends. |
| Use | Not |
|---|---|
| patron | user, customer, member, cardholder |
| card | ID card, badge, credential |
| add funds · top-up (record) | deposit, load, reload, recharge |
| suspend / reactivate | block, freeze, disable |
| sign in / sign out | log in, login (verb) |
| AVRO One, GoPrint, ITC Systems | AVRO1, Go Print, ITC |
BeforeError 500: Request failed.
AfterCouldn’t load transactions. Try again in a minute.
BeforeAre you sure?
AfterSuspend Jordan Lee’s card?
BeforeSuccess!!
AfterChanges saved.
BeforeClick here to add money to your account
AfterAdd funds
BeforeNo data
AfterNo transactions this month.
For developers and their AI agents. We use stock Tailwind CSS v4 and add exactly one file: src/theme.css. Import it after Tailwind and every app gets the same tokens, global defaults and component classes. No plugins, no JS config, no wrapper library. Source: docs/tailwind.md.
@import "tailwindcss"; @import "@itc/avro-arrow-design-system/theme.css";
| Layer | In theme.css | What it does | You write |
|---|---|---|---|
| 1. Tokens | @theme { … } | Our colours, type scale, radius, shadows, fonts. Some keep Tailwind's names with our values (see overrides). | Nothing. Use the utilities they create (bg-surface, text-fg-muted, rounded-md). |
| 2. Global defaults | @layer base { … } | Rules for bare elements: page colours, font (Outfit everywhere), focus ring, tabular numbers, reduced motion. | Nothing. They apply automatically. |
| 3. Component classes | @layer components { … } | btn, input, card, table, badge, modal… built with @apply from the same utilities. | class="btn btn-primary". Never re-type the utilities a component class applies. |
| 4. Utilities | stock Tailwind | Layout and one-offs: flex, grid, gap-4, w-full, ml-auto, mt-6. | Utilities for layout around and between components. |
Utilities beat component classes (the utilities layer comes last), so class="btn btn-primary w-full" just works.
Tailwind recommends removing duplication with framework components or, for small, heavily repeated pieces, with component classes. ITC products run on different stacks, so a CSS class works everywhere and no wrapper library is needed.
Each component class is defined once, specs and gallery are generated from it, and npm run build compiles it with Tailwind so a broken @apply fails the build.
Add to the product repo's CLAUDE.md (see docs/consuming.md):
All UI MUST follow @node_modules/@itc/avro-arrow-design-system/AI-RULES.md Before building any UI, read the matching spec in node_modules/@itc/avro-arrow-design-system/components/.
AI-RULES.md tells agents to use component classes, never re-type their utilities, and leave <!-- DS-GAP: … --> when no component fits.
Same class name, different value. Watch for these if you know stock Tailwind. AVRO values are read live from the theme.
| Class | AVRO | Stock Tailwind v4 |
|---|
New (not in Tailwind): text-2xs, text-md, font-display (Outfit), all colours (ink-*, brand-*, accent-*, status, semantic surface/fg/line…), shadow-focus, ease-out-quint, max-w-content / max-w-form / max-w-prose. Spacing is unchanged (4px base).
Everything in @layer base, read live from the theme.
| Selector | Sets |
|---|
Every class in @layer components, read live from the theme. Open a component for examples and markup.
components/<name>.md). Compose with utilities only for layout.btn btn-primary w-full). Don't fork the class.@theme tokens in an app. Request a design-system change (docs/governance.md).p-[13px], text-[#333]) and no stock palette colours (gray-*, blue-*).button { … }) in an app. Global defaults live only in theme.css.Edit its rules in src/theme.css (under its /* @component name */ marker) → npm run build (regenerates the spec's class table and the gallery theme, compiles with Tailwind) → update the spec's prose and gallery examples if behaviour changed → CHANGELOG.md.
gallery/demo/ is a product screen built exactly this way: app.css holds only the two @import lines above, and npm run build compiles it with stock Tailwind into avro.css. The build fails if the demo uses inline styles, hex colours, arbitrary values or stock palette colours, so it can only ever show what this system provides. Open the demo
What changed in the design system, in plain English, newest first.
Upcoming enhancements to the design system.
Jordan Lee won't be able to make purchases or open doors until reactivated.
The file this page is built from. AI tools read this version.