Skip to content
Citia DS

Foundations

Colour

Semantic colour roles, status colours with words and icons, state layers, dark-mode rules, the primitive palette and the live contrast results for 72 pairs.

Colour works in three layers: primitives (raw hex values), semantic roles (primary, base-100, error) and Citia extensions (--ds-link, --ds-focus). Components use only the last two. Every role has a value in the light and the dark theme, and the swatches below are rendered live from tokens.json, so they are always current.

The source is tokens/tokens.json. Never edit Styles/generated/tokens.css by hand; run dotnet run tools/tokens.cs.

Semantic roles

The daisyUI 5 roles. Each swatch shows light on the left and dark on the right. Use them as var(--color-primary) or as Tailwind utilities (bg-primary, text-base-content).

--color-base-100#f9f9f6#151c25 Surface: cards, tables, inputs, document sidebar.
--color-base-200#f1f2ee#0e141b Page background, table headers, read-only fields.
--color-base-300#dfe3dd#253040 Borders and dividers.
--color-base-content#0f172a#e6edf3 Primary text.
--color-primary#00838f#00a4b2 Primary actions, active navigation, brand focus.
--color-primary-content#ffffff#0e141b
--color-secondary#00767f#05666f Application sidebar background.
--color-secondary-content#ffffff#ffffff
--color-accent#00a4b2#00d4df Focus ring, active rail marker, charts.
--color-accent-content#0f172a#0e141b
--color-neutral#0f172a#e6edf3
--color-neutral-content#f6f8fa#0e141b
--color-info#3b6fd4#7aa2f7
--color-info-content#ffffff#0e141b
--color-success#047857#34d399
--color-success-content#ffffff#0e141b
--color-warning#c2410c#f59e0b
--color-warning-content#ffffff#0e141b
--color-error#dc2626#f87171
--color-error-content#ffffff#0e141b
Role Use
base-100 Surfaces: cards, tables, inputs, menus.
base-200 Page background, table headers, quiet fills.
base-300 Hairline borders and dividers (1px).
base-content Body text and icons on the base surfaces.
primary, primary-content The one primary action, selection, focus; text on top of it.
secondary, accent Sidebar surface and active markers. Not focus rings.
info, success, warning, error Status fills and borders (see below).

Surfaces are neutral and warm. Teal appears on the primary action, the current navigation item, the selection and the focus ring, and nowhere else by default.

Citia extensions

Roles that daisyUI does not have. CSS names are --ds-<name>; Tailwind reads --color-<name> (text-link, bg-popover, border-control).

--ds-control-border#7b8a94#74848f Border for inputs, selects and secondary buttons (≥ 3:1).
--ds-quiet#006a73--color-primary Quiet teal text: ghost buttons, eyebrows, empty-state icons.
--ds-link#2b5cc4--color-info Identity link (.ds-link-ref). Blue is only for links.
--ds-error-text#b91c1c--color-error
--ds-warning-text#9a3412--color-warning
--ds-popover--color-base-100mix Floating layer surface (dropdown, calendar, combobox).
--ds-popover-border--ds-control-border--ds-control-border
--ds-popover-hovermixmix
--ds-subtlemixmix Secondary text: field help, captions, meta labels.
--ds-mutedmixmix Tertiary text that still passes AA on base-100 (.text-muted).
--ds-focus#00838f#00d4df Focus ring. The older version used accent (#00a4b2 = 2.86:1 on base-100); the DS uses primary so it is ≥ 3:1 (WCAG 1.4.11).
--ds-scrimrgb(8 18 28 / .4)rgb(8 18 28 / .4) Modal backdrop (::backdrop).
--ds-excel#1d6f42#1d6f42
--ds-excel-hover#185c37#185c37
--ds-pdf#b30b00#b30b00
--ds-pdf-hover#8f0900#8f0900
--ds-syntax-tag#006780#66d9e8 XML/HTML tag.
--ds-syntax-attr#8c3a17#edba82 XML/HTML attribute.
--ds-primary-softmixmix Tonal primary fill for status surfaces (alerts, pills, chips, selected rows). Pair with --ds-primary-text or base content.
--ds-info-softmixmix Tonal info fill for status surfaces (alerts, pills, chips, selected rows). Pair with --ds-info-text or base content.
--ds-success-softmixmix Tonal success fill for status surfaces (alerts, pills, chips, selected rows). Pair with --ds-success-text or base content.
--ds-warning-softmixmix Tonal warning fill for status surfaces (alerts, pills, chips, selected rows). Pair with --ds-warning-text or base content.
--ds-error-softmixmix Tonal error fill for status surfaces (alerts, pills, chips, selected rows). Pair with --ds-error-text or base content.
--ds-neutral-softmixmix Tonal neutral fill: tags, inactive pills, inline code backgrounds.
--ds-info-text#2b5cc4--color-info Info text and icons on light surfaces (≥ 4.5:1).
--ds-success-text#047857--color-success Success text and icons on light surfaces (≥ 4.5:1).
--ds-primary-text#006a73--color-primary Primary-coloured text on light surfaces (≥ 4.5:1).
--ds-chart-1--color-primary--color-primary Categorical chart series 1. Graphic contrast on base-100 (≥ 3.9:1); use in this order, never as the only cue (label or legend).
--ds-chart-2#3a61c4#86a9f7 Categorical chart series 2. Graphic contrast on base-100 (≥ 3.9:1); use in this order, never as the only cue (label or legend).
--ds-chart-3#c26300#f0a646 Categorical chart series 3. Graphic contrast on base-100 (≥ 3.9:1); use in this order, never as the only cue (label or legend).
--ds-chart-4#c72c53#f6838e Categorical chart series 4. Graphic contrast on base-100 (≥ 3.9:1); use in this order, never as the only cue (label or legend).
--ds-chart-5#137738#6cd092 Categorical chart series 5. Graphic contrast on base-100 (≥ 3.9:1); use in this order, never as the only cue (label or legend).
--ds-chart-6#8048b6#be95ec Categorical chart series 6. Graphic contrast on base-100 (≥ 3.9:1); use in this order, never as the only cue (label or legend).
--ds-chart-7#576574#a0b0c1 Categorical chart series 7. Graphic contrast on base-100 (≥ 3.9:1); use in this order, never as the only cue (label or legend).
--ds-chart-8#857d0b#c9c261 Categorical chart series 8. Graphic contrast on base-100 (≥ 3.9:1); use in this order, never as the only cue (label or legend).
Token Use
--ds-subtle Secondary text: hints, captions, metadata (8.7:1 in light).
--ds-muted Tertiary text that still passes AA (6.04:1). Never for essential information.
--ds-control-border Borders of inputs, selects, secondary buttons. At least 3:1 against the surface (3.37:1 light, 4.44:1 dark).
--ds-link Links in running text and tables. Always underlined or otherwise marked.
--ds-error-text, --ds-warning-text Text on pale error or warning tint; the base colours are too light for small text in the light theme.
--ds-focus The 2px focus ring. The only focus colour.
--ds-popover, --ds-popover-border, --ds-popover-hover Floating layers: solid, not transparent.
--ds-scrim Modal backdrop.

Important

Blue is for links. Teal is for the primary action, selection and focus. Do not use a link colour on a button or a status.

Status colours

Status uses four hues with fixed meaning. A tonal (pale) fill carries the area; the saturated colour appears only as a 1px border, an icon or a dot. A status always includes a word, and the icon shape differs per tone, so it survives colour-blindness and greyscale.

Tone Meaning Icon Example word
info Neutral news, in progress info Scheduled
success Done, healthy check-circle Paid
warning Needs attention, not failed alert-triangle Awaiting payment
error Failed or destructive x-circle Overdue
Status with word and icon
Scheduled Paid Awaiting payment Overdue Draft
View HTML code
HTML
<div class="flex flex-wrap gap-3">
  <span class="ds-pill is-info">Scheduled</span>
  <span class="ds-pill is-success">Paid</span>
  <span class="ds-pill is-warning">Awaiting payment</span>
  <span class="ds-pill is-error">Overdue</span>
  <span class="ds-pill is-neutral">Draft</span>
</div>

Rules:

  • Write the status as a word. Colour and dot support it.
  • Text on a tint uses base-content or the -text role, never the base status colour in the light theme.
  • Do not mark an error with red alone: add the message beside the field, linked with aria-describedby.
  • Do not change a meaning to suit a brand. A brand may adjust a tone, not a meaning.

State layers

Hover, press, selection and drag are expressed as a translucent layer of the content colour over the surface, not as new colours. Tokens:

Token Value Used for
--ds-opacity-state-hover 0.06 Hover on rows, ghost buttons, menu items
--ds-opacity-state-pressed 0.10 Pointer down
--ds-opacity-state-selected 0.12 Selected rows, current item, chosen option (with the primary colour)
--ds-opacity-state-drag 0.16 Drop targets and dragged items
CSS
.app-row:hover {
  background: color-mix(in oklab, var(--color-base-content) calc(var(--ds-opacity-state-hover) * 100%), var(--color-base-100));
}
.app-row[aria-selected="true"] {
  background: color-mix(in oklab, var(--color-primary) calc(var(--ds-opacity-state-selected) * 100%), var(--color-base-100));
}

Because layers mix from tokens, they adjust to any theme with no new hex values. Tints stay at 16% or lower so text on them keeps 4.5:1.

Tint and border recipes

Purpose Range Example
Soft fill on a surface 6 to 12% color-mix(in oklab, var(--color-error) 10%, var(--color-base-100))
Soft fill on nothing 4 to 16% color-mix(in oklab, var(--color-primary) 10%, transparent)
Tone border 22 to 45% color-mix(in oklab, var(--color-info) 35%, var(--color-base-300))
Text selection 24% color-mix(in oklab, var(--color-primary) 24%, transparent)

Dark mode

The dark theme is designed, not inverted.

  • Surfaces are blue-black (base-100 #151c25, page #0e141b), not pure black, so shadows and hairlines still read.
  • primary becomes #00a4b2 with dark text #0e141b; light uses #00838f with white.
  • --ds-focus becomes aqua #00d4df (9.38:1) because the light focus colour is too dark on a dark page.
  • --ds-link moves to the info blue #7aa2f7 (6.81:1).
  • Status colours get lighter (success #34d399, error #f87171) and their -text roles equal the base colours, which already pass 4.5:1.
  • Popovers are lifted with a little white (color-mix with 6% white) so a layer separates from the page without relying on shadow; shadows are darker (.36) because the page already is.
  • Avoid pure white text (base-content is #e6edf3) and large saturated areas, which glare on dark.

Choose the theme in markup with data-theme="light" or "dark"; see Theming.

Primitive palette

The raw values behind the roles. Hover a chip for its note.

white

white#ffffff

teal

teal.500#00a4b2
teal.600#00838f
teal.700#00767f
teal.750#006a73
teal.800#05666f

aqua

aqua.400#00d4df

paper

paper.50#f9f9f6
paper.100#f1f2ee
paper.200#dfe3dd

steel

steel.400#7b8a94
steel.450#74848f

slate

slate.900#0f172a

frost

frost.50#f6f8fa
frost.100#e6edf3

night

night.700#253040
night.800#151c25
night.900#0e141b

blue

blue.400#7aa2f7
blue.500#3b6fd4
blue.600#2b5cc4

emerald

emerald.400#34d399
emerald.700#047857

amber

amber.500#f59e0b

orange

orange.700#c2410c
orange.800#9a3412

red

red.400#f87171
red.600#dc2626
red.700#b91c1c

excel

excel.600#1d6f42
excel.700#185c37

pdf

pdf.600#b30b00
pdf.700#8f0900

avatar

avatar.c0#00838f
avatar.c1#2b5cc4
avatar.c2#7c3aed
avatar.c3#c2410c
avatar.c4#047857
avatar.c5#be185d
avatar.c6#4d7c0f
avatar.c7#0e7490

brand

brand.deep#04222a
brand.ink#0b1720
brand.amber#ffb938

Warning

Components never use primitives. Write var(--color-primary), not #00838f or teal.600. When a theme changes, only the semantic roles are remapped; a component that used a primitive would be left behind.

Contrast results

dotnet run tools/tokens.cs computes the WCAG 2.2 ratio of 72 pairs in both themes and writes wwwroot/tokens/contrast.json. A required pair below 4.5:1 (text) or 3:1 (boundaries, focus ring, icons) fails the build. Pairs marked informational do not decide the result.

ThemePairSampleRatioMinimumResult
lightbase-content
Body text on surfaces
Aa 12
#0f172a / #f9f9f6
16.93:14.5:1Pass
lightbase-content / base-200
Body text on the page background
Aa 12
#0f172a / #f1f2ee
15.88:14.5:1Pass
lightsubtle
Secondary text (field help)
Aa 12
#404858 / #f9f9f6
8.70:14.5:1Pass
lightsubtle / base-200
Secondary text on the background
Aa 12
#404858 / #f1f2ee
8.16:14.5:1Pass
lightmuted
Muted text on surfaces
Aa 12
#5a5f6b / #f9f9f6
6.04:14.5:1Pass
lightmuted / base-200
Muted text on the background
Aa 12
#575d69 / #f1f2ee
5.87:14.5:1Pass
lightprimary-content
Primary button label
Aa 12
#ffffff / #00838f
4.52:14.5:1Pass
lightsecondary-content
Sidebar item
Aa 12
#ffffff / #00767f
5.39:14.5:1Pass
lightsidebar active
Active sidebar item
Aa 12
#ffffff / #005056
9.24:14.5:1Pass
lightsidebar group
Sidebar group header (11 px)
Aa 12
#e6f1f2 / #00767f
4.68:14.5:1Pass
lightaccent-content
Text on accent
Aa 12
#0f172a / #00a4b2
5.91:14.5:1Pass
lightinfo-content
Filled info alert or badge
Aa 12
#ffffff / #3b6fd4
4.76:14.5:1Pass
lightsuccess-content
Filled success badge
Aa 12
#ffffff / #047857
5.48:14.5:1Pass
lightwarning-content
Filled warning badge
Aa 12
#ffffff / #c2410c
5.18:14.5:1Pass
lighterror-content
Destructive button
Aa 12
#ffffff / #dc2626
4.83:14.5:1Pass
lightlink
Link on surfaces
Aa 12
#2b5cc4 / #f9f9f6
5.80:14.5:1Pass
lightlink / base-200
Link on the background
Aa 12
#2b5cc4 / #f1f2ee
5.44:14.5:1Pass
lightlink / row hover
Link in a hovered table row
Aa 12
#2b5cc4 / #eeefed
5.30:14.5:1Pass
lightquiet
Ghost button label
Aa 12
#006a73 / #f9f9f6
6.02:14.5:1Pass
lightquiet / ghost hover
Ghost button on hover
Aa 12
#006a73 / #dae4e3
4.89:14.5:1Pass
lighterror-text
Error message, soft error badge
Aa 12
#b91c1c / #f9f9f6
6.13:14.5:1Pass
lightwarning-text
Soft warning badge or alert
Aa 12
#9a3412 / #f9f9f6
6.93:14.5:1Pass
lightsuccess text
Active status (text on tint)
Aa 12
#047857 / #e8eee8
4.66:14.5:1Pass
lighterror text (raw)
Validation message using --color-error
Aa 12
#dc2626 / #f9f9f6
4.58:14.5:1Pass
lightpopover text
Text on floating layers
Aa 12
#0f172a / #f9f9f6
16.93:14.5:1Pass
lightexcel
Excel button
Aa 12
#ffffff / #1d6f42
6.17:14.5:1Pass
lightpdf
PDF button
Aa 12
#ffffff / #b30b00
7.08:14.5:1Pass
lightcontrol-border
Input / secondary button border (non-text)
Aa 12
#7b8a94 / #f9f9f6
3.37:13:1Pass
lightfocus ring
Focus ring (non-text)
Aa 12
#00838f / #f9f9f6
4.29:13:1Pass
lightfocus ring / base-200
Focus ring on the background
Aa 12
#00838f / #f1f2ee
4.02:13:1Pass
lightaccent ring (v1)
Version 1 focus ring — informational
Aa 12
#00a4b2 / #f9f9f6
2.86:13:1Info
lightprimary edge
Edge of active controls (chips, segments)
Aa 12
#00838f / #f9f9f6
4.29:13:1Pass
*avatar-c0
White initials on an avatar
Aa 12
#ffffff / #00838f
4.52:14.5:1Pass
*avatar-c1
White initials on an avatar
Aa 12
#ffffff / #2b5cc4
6.12:14.5:1Pass
*avatar-c2
White initials on an avatar
Aa 12
#ffffff / #7c3aed
5.70:14.5:1Pass
*avatar-c3
White initials on an avatar
Aa 12
#ffffff / #c2410c
5.18:14.5:1Pass
*avatar-c4
White initials on an avatar
Aa 12
#ffffff / #047857
5.48:14.5:1Pass
*avatar-c5
White initials on an avatar
Aa 12
#ffffff / #be185d
6.04:14.5:1Pass
*avatar-c6
White initials on an avatar
Aa 12
#ffffff / #4d7c0f
4.99:14.5:1Pass
*avatar-c7
White initials on an avatar
Aa 12
#ffffff / #0e7490
5.36:14.5:1Pass
darkbase-content
Body text on surfaces
Aa 12
#e6edf3 / #151c25
14.51:14.5:1Pass
darkbase-content / base-200
Body text on the page background
Aa 12
#e6edf3 / #0e141b
15.66:14.5:1Pass
darksubtle
Secondary text (field help)
Aa 12
#acb3ba / #151c25
8.08:14.5:1Pass
darksubtle / base-200
Secondary text on the background
Aa 12
#acb3ba / #0e141b
8.72:14.5:1Pass
darkmuted
Muted text on surfaces
Aa 12
#a3aab1 / #151c25
7.31:14.5:1Pass
darkmuted / base-200
Muted text on the background
Aa 12
#a1a8ae / #0e141b
7.66:14.5:1Pass
darkprimary-content
Primary button label
Aa 12
#0e141b / #00a4b2
6.13:14.5:1Pass
darksecondary-content
Sidebar item
Aa 12
#ffffff / #05666f
6.70:14.5:1Pass
darksidebar active
Active sidebar item
Aa 12
#ffffff / #02444b
10.84:14.5:1Pass
darksidebar group
Sidebar group header (11 px)
Aa 12
#e6f0f1 / #05666f
5.76:14.5:1Pass
darkaccent-content
Text on accent
Aa 12
#0e141b / #00d4df
10.12:14.5:1Pass
darkinfo-content
Filled info alert or badge
Aa 12
#0e141b / #7aa2f7
7.35:14.5:1Pass
darksuccess-content
Filled success badge
Aa 12
#0e141b / #34d399
9.63:14.5:1Pass
darkwarning-content
Filled warning badge
Aa 12
#0e141b / #f59e0b
8.62:14.5:1Pass
darkerror-content
Destructive button
Aa 12
#0e141b / #f87171
6.69:14.5:1Pass
darklink
Link on surfaces
Aa 12
#7aa2f7 / #151c25
6.81:14.5:1Pass
darklink / base-200
Link on the background
Aa 12
#7aa2f7 / #0e141b
7.35:14.5:1Pass
darklink / row hover
Link in a hovered table row
Aa 12
#7aa2f7 / #1c232c
6.30:14.5:1Pass
darkquiet
Ghost button label
Aa 12
#00a4b2 / #151c25
5.68:14.5:1Pass
darkquiet / ghost hover
Ghost button on hover
Aa 12
#00a4b2 / #1a2d36
4.74:14.5:1Pass
darkerror-text
Error message, soft error badge
Aa 12
#f87171 / #151c25
6.20:14.5:1Pass
darkwarning-text
Soft warning badge or alert
Aa 12
#f59e0b / #151c25
7.98:14.5:1Pass
darksuccess text
Active status (text on tint)
Aa 12
#34d399 / #1a282e
7.85:14.5:1Pass
darkerror text (raw)
Validation message using --color-error
Aa 12
#f87171 / #151c25
6.20:14.5:1Pass
darkpopover text
Text on floating layers
Aa 12
#e6edf3 / #202730
12.73:14.5:1Pass
darkexcel
Excel button
Aa 12
#ffffff / #1d6f42
6.17:14.5:1Pass
darkpdf
PDF button
Aa 12
#ffffff / #b30b00
7.08:14.5:1Pass
darkcontrol-border
Input / secondary button border (non-text)
Aa 12
#74848f / #151c25
4.44:13:1Pass
darkfocus ring
Focus ring (non-text)
Aa 12
#00d4df / #151c25
9.38:13:1Pass
darkfocus ring / base-200
Focus ring on the background
Aa 12
#00d4df / #0e141b
10.12:13:1Pass
darkaccent ring (v1)
Version 1 focus ring — informational
Aa 12
#00d4df / #151c25
9.38:13:1Pass
darkprimary edge
Edge of active controls (chips, segments)
Aa 12
#00a4b2 / #151c25
5.68:13:1Pass

Do and don't

  • Do use roles through variables: color: var(--ds-subtle), border-color: var(--ds-control-border).
  • Do pair every status colour with a word and an icon.
  • Do keep one btn-primary per area; teal marks it.
  • Do check any new text-on-colour pair and add it to tools/tokens.cs.
  • Don't use hex values, primitives or Tailwind palette classes (bg-red-500) in components.
  • Don't tint above 16% behind text, or use gradients, glows or large saturated blocks.
  • Don't use colour alone for state: selected rows also get a check, a bar or a weight change.
  • Don't use blue for buttons or teal for links.
CSS
.app-ref { color: var(--ds-link); text-decoration: underline; }
.app-hint { color: var(--ds-subtle); }
.app-field { border: 1px solid var(--ds-control-border); background: var(--color-base-100); }
Razor (.cshtml)
<a asp-page="Details" asp-route-id="@Model.Id" class="text-link underline underline-offset-2">INV-2026-0418</a>
<p class="text-subtle">Format INV-YYYY-NNNN</p>
<input class="input border-control" />

Those utilities come from generated/tailwind-theme.css and resolve per element, so a nested data-theme keeps its own colours.

Citia Design System 2.0.1 · Citia Engineering Indonesia. Derived from INTEGRA TPS Integration; built for every Citia web application.