Pages

Skip to content

Design ​

The look of wing(ui) comes from CSS variables (tokens) and three CSS files. This page covers what each file does, the kinds of tokens, and the color rules.

Stylesheets ​

tokens.cssColors, spacing, and other variablesbase.cssStyles body, links, and imagesprose.cssStyles article textRequiredOptionalOptionalComponentsUiButton, UiModal, …Your app's elementsbody, a, img, …Article textInside .prose
Components need tokens.css. Add base.css and prose.css when you need them
  • tokens.css (required): only CSS variables for colors, spacing, corner radii, and so on. Load it whenever you use the components.
  • base.css (optional): styles body, links, images, and the focus outline.
  • prose.css (optional): styles headings, paragraphs, lists, tables, and more inside .prose for articles.

Components carry the base styles their own elements need, so they look the same without base.css. Use base.css when you want your app's own body and links to match the library as well.

base.css and prose.css use the variables in tokens.css. Load tokens.css whenever you load either of them.

Choose by use case:

  • Components only: tokens.css
  • Matching the whole page: tokens.css and base.css
  • Publishing articles: tokens.css, base.css, and prose.css

The Nuxt Layer loads tokens.css automatically, and the wingUi.css option loads base.css and prose.css. See the Introduction for how to load them.

Tokens ​

Tokens are the CSS variables that tokens.css defines on :root. Values an app is likely to change, such as colors, corner radii, and the focus outline, are set with tokens inside the components too.

The color tokens are covered in Colors. The other tokens are:

VariableValueUse
Typography
--font-bodyInter, 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Noto Sans JP', 'Noto Sans CJK JP', 'Yu Gothic', Meiryo, sans-serifBody font. Inter for Latin text, the system fonts for Japanese
--font-monoui-monospace, 'SFMono-Regular', Consolas, monospaceCode font
--font-size-body16pxBody text size
--font-size-small14pxText size for secondary text and small parts
--line-height-body1.8Body line height
Width and spacing
--width-prose40emMaximum line length of body text
--width-site1120pxMaximum page width
--gutterclamp(20px, 4vw, 64px)Space between the screen edge and the content. Changes with the screen width
--space-1--space-2--space-3--space-4--space-5--space-6--space-8--space-12--space-164px8px12px16px20px24px32px48px64pxSpacing steps. The number is a multiple of 4px
Control size and shape
--control-min44pxMinimum height of buttons and inputs, large enough to tap
--control-dense32pxSmaller height for dense places such as toolbars and menu rows
--radius-control4pxCorner radius of buttons, inputs, and cards
--radius-popover6pxCorner radius of floating panels such as menus, dialogs, and toasts
--shadow-popover0 6px 20px rgb(0 0 0 / 12%)Shadow of floating panels
Focus and motion
--focus-ring-width2pxWidth of the focus outline
--focus-ring-offset4pxGap between the focus outline and the element
--duration-fast120msDuration of hover color changes
--easing-standardease-outEasing of changes
Stacking order
--z-sticky--z-popover--z-toast102040Sticky header, floating panels, then toasts, from bottom to top

Changing values ​

To change a token, redefine the variable with the same name in your own CSS. Load it after tokens.css.

css
:root {
  --color-accent: #2f5f86;
  --radius-control: 6px;
}
:root.dark,
:root[data-theme='dark'] {
  --color-accent: #93b8d8;
}

The dark colors are defined on :root.dark and :root[data-theme='dark']. When you change a color, redefine its dark value with the same selectors. Changing only :root leaves the original value in the dark theme.

Colors ​

Color roles ​

--color-canvas--color-text--color-muted--color-field--color-surface--color-line--color-accent--color-on-accent
Which color variable is used where on the screen

Color variables are named for their use, not for the color itself. For example, --color-accent is currently a wine color, but the name means "the color for emphasis". Changing the value to blue does not change where it is used.

VariableLightDarkUse
--color-canvas#f5f5f3#19191bPage background. Also the background of floating panels such as menus and dialogs
--color-field#fcfcfb#212124Inside of inputs. A step brighter than the page so empty fields stand out
--color-surface#e9e9e6#2c2c30Hover backgrounds, table header rows, and lightly filled parts
--color-line#d5d5d3#3e3e43Borders and dividers
--color-muted#66666a#a6a6aeSecondary text, dates, and icons
--color-text#242426#eeeef0Body text. Also the tooltip background
--color-accent#843b51#d48ba2Selected items, the focus outline, primary buttons, and hovered links
--color-on-accent#f5f5f3#19191bText on areas filled with the accent color

Keep the accent color to places that should draw the eye, such as the selected item, focus, and primary buttons. Do not use it for large areas such as the page background.

Status colors ​

Notices, success, warnings, and errors each pair a text and icon color with a light background. UiAlert, UiBadge, toasts, and others use these pairs as tone.

VariableLightDarkUse
--color-info--color-info-surfaceAa#2f5f86#e2ebf3Aa#93b8d8#1f2b36Notices and extra information
--color-success--color-success-surfaceAa#2f6b45#e3eee6Aa#8fc4a0#213027Completion and success
--color-warning--color-warning-surfaceAa#7a5a12#f3ead6Aa#d9b56a#352d1dStates that need attention
--color-danger--color-danger-surfaceAa#a12f36#f6e4e3Aa#f0a091#3a2426Errors and actions that cannot be undone, such as deleting

Some people find colors hard to tell apart, so never show a status by color alone. Show an icon or text with it, as UiAlert and UiBadge do.

Light and dark ​

<html><html class="dark">
Adding the dark class to <html> switches the same variables to their dark values

tokens.css has two sets of values, light and dark. The dark values apply only when <html> has the dark class or data-theme="dark".

tokens.css does not follow the OS setting (prefers-color-scheme) on its own. Choosing the theme and setting it on <html> is up to the app.

In Nuxt, turning on wingUi.colorMode makes the Nuxt Layer do this with @nuxtjs/color-mode. It reads the saved theme or the OS setting and adds the dark class to <html> before the first paint. Bind UiThemePicker to preference from useColorMode().

vue
<script setup lang="ts">
const colorMode = useColorMode()
</script>

<template>
  <UiThemePicker v-model="colorMode.preference" />
</template>

In Vue, UiThemePicker only reports whether system, light, or dark was chosen. Checking the OS setting when "system" is chosen is also up to the app. These docs switch the theme with this function:

ts
function applyTheme(mode: 'system' | 'light' | 'dark') {
  const dark =
    mode === 'dark' ||
    (mode === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches)
  document.documentElement.classList.toggle('dark', dark)
}

The dark values come with color-scheme: dark, so scrollbars and default form controls also follow the theme.

Code colors ​

VariableLightDarkUse
--color-code-background#f0ecee#242126Code block background
--color-code-foreground#3b353d#ddd7dbCode text
--color-code-comment#70646f#a198a0Code comments

Light and dark Shiki themes are provided for syntax highlighting. Pass syntaxThemes from @wing-land/ui/theme/shiki to your Shiki or Nuxt Content settings. The background, text, and comment colors match the --color-code-* values above.

ts
import { syntaxThemes } from '@wing-land/ui/theme/shiki'

// For Shiki's codeToHtml() and similar
const themes = { light: syntaxThemes.default, dark: syntaxThemes.dark }

The Nuxt Layer sets these themes for Nuxt Content automatically. See the Introduction.

Typography ​

Latin text uses Inter, Japanese text uses the device's system fonts (Hiragino Kaku Gothic, Noto Sans JP, and others), and code uses a monospace font.

Inter can be loaded as a web font: turn on wingUi.font in Nuxt, or load @wing-land/ui/fonts.css in Vue. Only the Latin characters are loaded. Without it, Inter is used if the device has it; otherwise the fonts listed after Inter in --font-body are used.

Body text is 16px with a line height of 1.8. To keep lines from getting too long, body text is at most --width-prose (40em) wide.

For articles, prose.css sets the size and spacing of headings, paragraphs, lists, quotes, tables, images, and code inside .prose, tuned for Japanese text. For example, paragraphs are 1.5em apart, and lines break so that punctuation and small kana such as 「ゃ」 never start a line.

When you place components or your own parts in an article, add the ui-not-prose class to them. prose.css does not apply inside it, so article styles never change how those parts look.