AppsCode Design System
The design system for every AppsCode console (cloud-ui, cluster-ui, kubedb-ui, platform-ui, billing-ui, selfhost-ui…): a Tailwind CSS v4 theme and Vue 3 components, with a documentation site that shows every component live.
It replaces the old Bulma/SCSS library (@appscode/design-system 2.x). Every component the apps import from the old library has a replacement here, and the docs' Migration page maps the old props to the new ones.
npm install
npm run dev # docs site at http://localhost:5173
What's in it
| Theme | src/lib/theme.css: colour scales, semantic tokens (text, borders, surfaces), type scale, radii, shadows and a full dark theme, as a Tailwind @theme. |
| Components | 101 Vue components (counting sub-parts such as AcDropdownItem) in eight groups, listed below. |
| Editors | AcCodeEditor and AcFileEditor on CodeMirror 6, in the /editor entry so other apps don't bundle CodeMirror. |
| Charts | Seven charts on Apache ECharts 6 (cloud-ui's engine), in the /charts entry. echarts is an optional peer dependency. |
| Composables | useColorMode() (light/dark/system), useFontScale() (text size), useToast() (notifications), useBrandColor() (re-hue the brand at runtime), useTablePagination() and useApiPagination(), useBulkSelection(), useFormValidation(), and copyText(). |
| Logo | AcLogo: the AppsCode logo and mark as inline SVG that follows dark mode. |
| Docs site | Getting started, foundations, a page per component (live playground, examples, guidelines, accessibility, API tables) and full example pages. |
Components
| Group | Components |
|---|---|
| Element | Button, Button Group, Badge, Tag, Avatar, Alert, Banner, Card, Divider, Kbd, Copy Button, Command Block, Check List |
| Feedback | Spinner and Preloader, Loader, Skeleton, Progress, Circular Progress, Toast, Empty State, Error State |
| Form | Input, Number Input, Textarea, Code Editor, File Editor, Select, Combobox, Tag Input, CheckBox, CheckRadio, Radio Card, Switch, Switch Card, Search Bar, File Upload, Slider, Date Picker, Month Picker, Color Input, Time Picker, Form, Form Array, Key-Value Input, Taint Input, Theme Mode, Font Scale |
| Overlay | Modal, Delete Modal, Confirm Modal, Side Panel, Dropdown, Popover, Tooltip |
| Layout | Page, Sidebar, Navbar, Header, Product Hero, Content Table, Section Content, Settings Row, Accordion, Status Bar, Footer |
| Navigation | Breadcrumb, Tabs, Steps, Pagination, Locale Switcher, Side Tabs, App Switcher, Cluster Switcher, Notifications |
| Data | Table, Bulk Action Bar, Info Table, List, Cell Value |
| Cards | Stat Card, Feature Card, Resource Card, Usage Card, Cost Summary |
Charts (/charts) | Line Chart, Area Chart, Bar Chart, Donut Chart, Map Chart, Graph Chart, Tree Chart |
Example pages put them together: a database list, a database detail page, a create wizard, settings, empty and error states, and a full app shell.
Look and feel
The look and voice are the AppsCode Cloud console's (cloud-ui), so the system fits cloud-ui, kubedb-ui, cluster-ui and platform-ui alike.
- Type: Roboto for the interface, Google Sans Flex for headings and JetBrains Mono for code and resource names, on a 14px base.
- Colour: white cards on a
#f9fafbpage, neutral greys (#252525text,#5c5c5cdescriptions, one#ebebebborder), the#00a651brand green, and success, info, warning, danger and pending statuses, each with an AA-strongtext colour. - Shape: 42px buttons and fields with a 10px radius, 14px cards and 16px dialogs. Cards rest on a border; shadows mark only what floats.
- Focus: a
focus-ringutility (primary border and a 2px halo) on form fields, and a 2px offset ring on buttons. - Dark mode: class-based. Every token has a dark value, so components need no dark-specific code.
- Icons: Lucide (
@lucide/vue). Components never draw their own SVG icons.
Using it in an app
The package is @ac-design/design-system on npm. npm run build:lib writes it to dist/lib, and npm pack makes an installable tarball.
npm install @ac-design/design-system @lucide/vue
npm install echarts # only if you use /charts
Styling rule
Style with Tailwind utility classes and the design tokens. Don't write custom CSS: no <style> blocks in components and no one-off class rules. Use arbitrary values (max-w-[320px], bg-[color-mix(in_srgb,var(--color-primary)_6%,var(--color-surface))]) for exact values, and arbitrary variants ([&_.cm-content]:p-2) for markup you don't own. If something truly can't be a utility, add a token or @utility to theme.css. The full rule is in CLAUDE.md.
There are two ways to load the styles:
| The app… | Load | Tailwind in the app |
|---|---|---|
| runs Tailwind v4 (cloud-ui, cluster-ui, kubedb-ui, platform-ui and new apps) | theme.css | Yes |
| still loads the old Bulma/SCSS design system and has no Tailwind | compat.css | No |
In an app with Tailwind v4
// main.ts: fonts once
import "@fontsource-variable/roboto";
import "@fontsource-variable/google-sans-flex/opsz.css";
import "@fontsource-variable/jetbrains-mono";
/* your main CSS file */
@import "tailwindcss";
@import "@ac-design/design-system/theme.css";
In an app with only the old styles
The docs page Getting Started → Existing Apps has the full guide. In short:
// main.ts (Vite), or the `css` list in nuxt.config.ts: after the old styles
import "./assets/styles/scss/main.scss";
import "@fontsource-variable/roboto";
import "@fontsource-variable/google-sans-flex/opsz.css";
import "@fontsource-variable/jetbrains-mono";
import "@ac-design/design-system/compat.css";
compat.cssis the library's styles, precompiled and fenced in: they apply only inside new components, and win over the old!importantutilities there. Old pages don't change; this was checked pixel by pixel in kubedb-ui.- Don't put utility classes on new components while both are loaded. Wrap the component in your own element for spacing.
Either way
import { AcButton, AcInput, useToast } from "@ac-design/design-system";
import { AcCodeEditor, AcFileEditor } from "@ac-design/design-system/editor";
import { AcLineChart, AcDonutChart } from "@ac-design/design-system/charts";
- Requirements: Vue 3.5+, Vite 5+ or Nuxt 3+, and TypeScript with
moduleResolution: "bundler". Every component renders on the server, so Nuxt SSR works. - Peer dependencies:
vue^3.5 and@lucide/vue^1.0, plusecharts^6 (optional) for/charts. CodeMirror, Ajv andyamlare regular dependencies, bundled only by apps that import from/editor. - Dark mode: put
.darkon<html>(the old.is-dark-themeclass works too), or calluseColorMode().setMode("dark" | "light" | "system"). The user's choice is saved under the oldthemeModelocalStorage key. - Toasts: mount
<AcToaster />once near the root, then calluseToast().success("Saved")anywhere. - Brand colour:
useBrandColor().setColor("#0066cc")re-hues the primary scale. The oldHexToHSL,setThemeHSLandgetThemeHSLhelpers are exported under the same names. - Router links: components with
toprops use vue-router'sRouterLinkwhen the app registers it. The library itself doesn't depend on vue-router.
Scripts
| Command | What it does |
|---|---|
npm run dev | Regenerates the API data, then starts the docs site at http://localhost:5173 |
npm run build | Regenerates the API data, type-checks, and builds the docs site into dist/ |
npm run preview | Serves the built docs site from dist/ |
npm run build:lib | Builds the library into dist/lib: ES modules, .d.ts files, theme.css and compat.css |
npm run type-check | vue-tsc over the whole project |
npm run meta | Regenerates src/docs/generated/component-meta.json (the API tables) from the components' types |
There is no test runner, ESLint or Prettier config yet. Type-check and both builds must pass, and changes are checked in the browser in light, dark and phone widths.
Project layout
src/
lib/ the design system (what gets published)
theme.css tokens as a Tailwind @theme, the dark theme, utilities and keyframes
index.ts main entry: every component, composable and public type
components/ AcButton.vue, AcInput.vue, … one file per component (sub-parts too)
index.ts component exports, sorted
types.ts shared types (Tone, Variant, Option, SelectOption)
editor/ the /editor entry: index.ts, CodeMirror theme, YAML/JSON Schema checks
charts/ the /charts entry: index.ts, ECharts setup, ChartFrame.vue (legend, tooltip, sr-only table)
composables/ useColorMode, useFontScale, useToast, useBrandColor, pagination, selection and form helpers
docs/ the documentation site
nav.ts every page: path, title, description, section, group, component
router.ts hash router built from nav.ts
pages/ getting-started/, foundations/, components/, examples/
examples/<slug>/*.vue one runnable file per example, shown live with its source
snippets/<page>/*.txt code samples shown on pages, loaded with ?raw
components/ doc building blocks: ComponentPlayground, ComponentExample, ApiTables, DoDont…
layout/ header, sidebar, table of contents, search
generated/ component-meta.json, written by scripts/gen-meta.mjs (don't edit)
scripts/
gen-meta.mjs reads component types with vue-component-meta → API tables
copy-theme.mjs ships theme.css with the library build
build-compat.mjs builds compat.css for apps that still load the old styles
public/ favicon, logos
vite.config.ts docs site
vite.lib.config.ts library build (three entries: index, editor/index and charts/index)
Contributing
Read CLAUDE.md first. It's the full guide to the codebase, written for people and coding agents alike: context, structure, component and docs patterns, the writing style, and what not to do.
The short version for a new component:
- Write
src/lib/components/AcThing.vuewith<script setup lang="ts">, a JSDoc line on every prop, and theme tokens only. Export it fromsrc/lib/components/index.ts, and its public types fromsrc/lib/index.ts. - Add examples as files in
src/docs/examples/thing/, importing from@/lib. - Add
src/docs/pages/components/ThingPage.vueand register it insrc/docs/nav.ts. - Run
npm run dev. The API tables regenerate from your types. - Add old → new rows to the Migration page if it replaces something from the old library.
- Check light, dark and a 375px phone width, then run
npm run buildandnpm run build:lib.
Commit with git commit -s.