Skip to content
NYCU-WinLabPublic

About

WinLab design system: a lightweight shadcn registry served at ui.winlab.tw

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

90 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

██╗    ██╗██╗███╗   ██╗██╗      █████╗ ██████╗     ██╗   ██╗██╗
██║    ██║██║████╗  ██║██║     ██╔══██╗██╔══██╗    ██║   ██║██║
██║ █╗ ██║██║██╔██╗ ██║██║     ███████║██████╔╝    ██║   ██║██║
██║███╗██║██║██║╚██╗██║██║     ██╔══██║██╔══██╗    ██║   ██║██║
╚███╔███╔╝██║██║ ╚████║███████╗██║  ██║██████╔╝    ╚██████╔╝██║
 ╚══╝╚══╝ ╚═╝╚═╝  ╚═══╝╚══════╝╚═╝  ╚═╝╚═════╝      ╚═════╝ ╚═╝

WinLab UI

One design system for every WinLab app, installed with the shadcn CLI.

CI  Live  License: MIT

WinLab apps used to pick their own colors, spacing and component variants. This registry is the single source for all of them: a lightweight set of tokens and components, each with only the variants our apps actually use.

Browse it live

Use it

Create a Next.js app, then init it from the WinLab base:

npx create-next-app@latest my-app
cd my-app
npx shadcn@latest init https://ui.winlab.tw/r/base.json

The base sets the style to winlab, so every component comes from this registry: a bare npx shadcn@latest add button fails on purpose. (init -t next also fails, because the template adds shadcn's own button.)

This writes the WinLab tokens into app/globals.css and registers the @winlab namespace in components.json, so later components install with:

npx shadcn@latest add @winlab/button

Moving an existing shadcn (Radix) app over: see MIGRATING.md for the install steps, what replaces card and sidebar, the Base UI behavior that changes, and test setup.

What is in the registry

Item Type What it gives you
base registry:base Ultramarine brand tokens (Pantone 286 C, #0033A0) on a neutral scale, light and dark; success and warning status colors; --radius: 1rem; the font stacks below
font-inter registry:font Inter for Latin text and numbers
font-noto-sans-jp registry:font Noto Sans JP for kanji and punctuation (Japanese forms: ,。 sit in the lower left)
font-noto-sans-tc registry:font Noto Sans TC for the Traditional Chinese characters Noto Sans JP lacks
font-jetbrains-mono registry:font JetBrains Mono for code and IDs, ligatures on
button registry:ui 40px tall; variants default, outline, ghost, destructive; sizes default, icon
label registry:ui Form label
input registry:ui Text input, same height as button
textarea registry:ui Multi-line input that grows with its content
skeleton registry:ui Loading placeholder
dialog registry:ui Modal; sizes default (512px) and wide (672px), scrolls past 92% of the viewport height
alert-dialog registry:ui Confirmation with Cancel and an action
select registry:ui Single-choice dropdown, same height as input
table registry:ui Data table with muted headers; add tabular-nums and text-right to number columns
popover registry:ui Frosted menu surface anchored to a trigger, 288px wide
command registry:ui Searchable list for a combobox inside a popover
combobox registry:ui ComboboxTrigger (same field look as select) and ComboboxContent (at least as wide as the trigger) around a command
dropdown-menu registry:ui Action menu: items, labels, separators and submenus
calendar registry:ui One month of 40px days to pick a single date; weeks start on Monday, captions read 2026 年 10 月
attachment registry:ui One file per row: 40px icon or thumbnail, name, a detail line and actions; states idle (dashed), uploading, error, done
checkbox registry:ui Round 20px checkbox
switch registry:ui On / off toggle, 40 x 24
tabs registry:ui Segmented tabs
browser registry:ui A browser window with the address in its title bar, framing a picture of a web page drawn with the site's tokens; same edge and corners as terminal
terminal registry:ui A terminal window: monospace rows of 1.25rem, two-cell East Asian characters, the dark terminal-* palette (window and shell text) in both themes
avatar registry:ui Round 40px avatar with initials fallback
tooltip registry:ui Short inverted label on hover or focus
separator registry:ui Hairline divider
collapsible registry:ui Unstyled show / hide region
sonner registry:ui Toasts: frosted surface, status-colored icons, action and cancel buttons
number-ticker registry:ui A whole number whose digits roll to their new value
progress registry:ui How much of a whole is used: an 8px muted track with a primary fill
chart registry:ui Recharts in WinLab colors: series take primary, muted-foreground or a state color; inverted tooltip and a legend
app-shell registry:block The four corners of every WinLab app (pages fold into a menu on narrow screens; the account can be a link or a menu) and the page layouts column, spotlight and wide; set NEXT_PUBLIC_BUILD_SHA, _TIME and _URL at build for the version corner
theme-provider registry:block Wrap the app in it: the theme follows the system (no toggle), D switches themes while checking a page; give <html> suppressHydrationWarning
confirm-dialog registry:block Asks before an action runs; the confirm button names the verb, both buttons lock while it runs, and it closes only when the action finishes
form-dialog registry:block A short form in a dialog; FormField puts a label over its control, fields and buttons lock while it submits, and it closes only when the submit finishes
empty-state registry:block What a list shows with no rows: "還沒有{noun}" or "找不到符合「{query}」的{noun}", with the next action; TableEmpty is the same sentence as a table row
page-header registry:block The top of a page: text-title title, and the page's own actions on the right (under the title on a phone); SectionHeader is the same one level down
section registry:block One group on a page instead of a card: a border-t divider, a title (h2) and the group's actions
list-skeleton registry:block Placeholder rows for a list (ListSkeleton) or a table body (TableSkeleton) on first load only, split by the same dividers as the real rows
member-combobox registry:block Pick lab members by name or email; one member closes on choice, multiple keeps the menu open and toggles
field-list registry:block One record's fields as a dl: muted names in a left column, values beside them (under them on a phone), split by dividers
status-page registry:block A spotlight page that says what happened and the way out: not found, an error, no access; the site's own 404 uses it
consent registry:block A spotlight page where a member lets a program act for them, such as an MCP client signing in; a plain form posting allow=yes or allow=no
date-picker registry:block A date field with the same closed look as select; opens a calendar, closes on choice, can block days, and posts yyyy-mm-dd under name for form-dialog
action-panel registry:block A page whose job is one action, such as opening the door: one 192px round button that locks while it runs, turns success for a moment, and says why when it cannot be pressed
file-upload registry:block Drop, paste or pick files; chosen files list as attachment rows to remove, files outside accept or maxSize show as errors with the reason, and name posts them inside form-dialog
signature-pad registry:block Draw or upload a handwritten signature in a dialog; black ink on white in every theme, exported as a cropped transparent PNG
focus registry:block The one loud thing on a page, under its header: a label, a title with a highlight and a row of facts
avatar-stack registry:block Several people in the space of one; the avatars spread apart when pointed at
countdown registry:block Time left until a moment, to the minute, with rolling digits
claude-code registry:block Claude Code's screen in a terminal for demos and install guides: welcome, prompts, MCP calls, permission questions, the /plugins menus (marketplaces, the plugin list with its search box, plugin details) and /mcp; content through props; colors are claude-* tokens from Claude Code's dark theme
typing registry:lib Timed keystrokes for typing animations: a steady pace (100ms a key, about 120 WPM) with jitter; Chinese typed as zhuyin that turns into the character on its tone key; InputBox shows the zhuyin with composing
expand-row registry:block A list row that opens in place to show its details
badge registry:ui Status label; variants default, muted, outline, destructive, success, warning

Text falls back in that order: font-sans is Inter, then Noto Sans JP, then Noto Sans TC. font-mono puts JetBrains Mono in front of the same CJK fonts. Use Inter's tabular-nums for amounts and table figures, not font-mono.

Type scale

Two sizes, named by role:

Utility Size / line height Use
text-title 24px / 32px Page and container titles
text-body 16px / 24px Everything else; secondary text uses text-muted-foreground

The base clears Tailwind's default scale (--text-*: initial), so text-sm, text-2xl and the rest generate nothing. Components added to this registry use text-title and text-body only. Hierarchy below a title comes from weight, the muted color and layout patterns, not size.

bun run tokens:check rejects anything that bypasses the tokens in this repo: the old scale names, arbitrary values (rounded-[10px], text-[13px]), Tailwind palette colors (bg-blue-500), color literals, inline styles and raw font-size.

Note

A project created with -t next keeps the template's Geist imports in app/layout.tsx. They are unused by the WinLab stacks; delete them to skip the extra download.

Design rules

The two-layer model, type, radius and color rules are in DESIGN.md.

Tech stack

Layer Choice
Framework Next.js 16 (App Router, static export)
Registry shadcn CLI (shadcn build)
Styling Tailwind CSS v4 + Base UI
Package manager Bun

Getting started

git clone https://github.com/NYCU-WinLab/ui && cd ui
bun install
bun dev

Registry items are declared in registry.json. bun run build runs shadcn build into public/r/ and then builds the site.

The site's own app/globals.css must match the base item's cssVars; bun run theme:check fails when they drift.

Deploy

The site is a static export served by nginx. Each push to main publishes ghcr.io/nycu-winlab/ui:main and ghcr.io/nycu-winlab/ui:sha-<commit>.

cp .env.example .env   # pin UI_VERSION to a sha- tag in production
docker compose pull && docker compose up -d

Contributing

Issues and PRs welcome: start with CONTRIBUTING.md.

License

MIT · Ultramarine, all the way down.

About

WinLab design system: a lightweight shadcn registry served at ui.winlab.tw

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages