ng-hub-ui-ds

The shared design token foundation of the ng-hub-ui family: the CSS variables --hub-ref-* (primitives) and --hub-sys-* (semantic), with light/dark and 8 themes. Import it once and the whole family reads the same palette.

What it is and what it is for

Its job is to unify and wire up the style variables across every library in the family. Each library is themed with --hub-* variables but does not define them: it consumes them. This package is the single source of truth that connects them, so that:

  • One palette feeds panels, forms, calendar, board… at the same time.
  • You re-theme once (one token) and the change reaches the whole family.
  • Dark mode and 8 themes are ready, switchable with one attribute.
  • You use it in your own CSS too, so your UI matches the components.

The wiring is rolling out library by library: further down, under the family, is the integration status of each one. Looking for the full token catalogue? It is in the token reference.

Getting started

The whole route: what to install, what to import and where, and which mechanism to use for each kind of change. ds has no dependencies (plain CSS/SCSS) and emits nothing unless you import it.

1 · Install

ds is the base; add the family libraries you use. None of them defines the palette — they all consume the ds tokens.

npm install ng-hub-ui-ds
npm install ng-hub-ui-panels ng-hub-ui-forms …   # the ones you use

2 · Import — once, at the root, and in this order

/* styles.scss (application root) */

/* 1 · Tokens — ALWAYS, exactly once: the base the whole family consumes */
@use 'ng-hub-ui-ds/styles/tokens/hub-tokens';

/* 2 · Native-element reset — only if your app does not ship one already (Bootstrap Reboot…) */
@use 'ng-hub-ui-ds/styles/base/reset';

/* 3 · Utilities — opt-in: import only the sheets you use
      (Bootstrap-exact names: do NOT load them next to a global Bootstrap) */
@use 'ng-hub-ui-ds/styles/utilities/layout';
@use 'ng-hub-ui-ds/styles/utilities/text';
@use 'ng-hub-ui-ds/styles/utilities/surfaces';

/* 4 · Sheets of their own shipped by some libraries — once, path documented by each README */
@use 'ng-hub-ui-forms/styles' as hub-forms;  // shared forms chrome
@use 'ng-hub-ui-utils/styles/tooltip';       // utils tooltip

/* 5 · Mixins — when you build your own components with the same system */
@use 'ng-hub-ui-ds' as hub;
.page { @include hub.center($max: xl); }

It works without Sass too: every sheet has a compiled .css twin — @import 'ng-hub-ui-ds/styles/tokens/hub-tokens.css'; — or through angular.json:

"styles": [
  "node_modules/ng-hub-ui-ds/styles/tokens/hub-tokens.css",
  "src/styles.scss"
]

3 · Modify — which mechanism for what you are changing

I want to…MechanismWhere it is explained
Change a global colour (the primary, say):root { --hub-sys-color-primary: … } — re-themes the whole familyHow to modify it
Add a new accent (brand, tertiary…)CSS: --hub-sys-color-brand · Sass: $hub-accents-extraHow to modify it
Drop variants I do not useSass: replace the map — with ($hub-accents: (primary: …, danger: …)) — and only those families are emittedSCSS functions
Adjust spacing / radii / gridOverride the structural tokens --hub-ref-space-* / --hub-sys-radius-* / --hub-sys-grid-*Structural layer
A whole theme (dark, corporate…)[data-theme='…'] { the tokens that differ } + the attribute on <html>Build your theme
Build MY components with the same systemMixins: @include hub.stack() · hub.text-bg(brand) · hub.radius(lg)… — or the utility classesMixins and styles
Live alongside Bootstrap / Material / TailwindBridge mixins: @include hub.bridge-bootstrap($mode: adopt | project)Bridges
Fine-tune ONE component of ONE libraryIts own --hub-<component>-* hooks (scoped or global)Token reference and each library's own page

4 · And the rest of the libraries

With the tokens loaded there is nothing else to configure: every ng-hub-ui-* reads --hub-sys-* and re-themes itself with your overrides. Each component also exposes --hub-<component>-* hooks for fine tuning, and some libraries ship sheets of their own, imported once next to the tokens — the exact path is documented by each README (the shared forms chrome, say, or the utils tooltip/overlay, as in the step 2 block). The integration status of every library is under the family.

Architecture: the two layers

ref

--hub-ref-*

Raw values with no context: colour ramps, spacing, radii, typography.

--hub-ref-color-blue-500
sys

--hub-sys-*

Meaningful assignments, the ones components consume.

--hub-sys-color-primary

Golden rule: components reference sys tokens only; sys points at ref. Changing a sys re-themes; changing a ref adjusts the base palette.

Semantic colours · live themes

The set is open: 9 variants by default (5 chromatic + 4 neutral) plus the ones you add. Every accent exposes a uniform family of roles — subtle, border-subtle, emphasis and the contrast pair on. Switch the theme and watch the whole palette recolour.

primary
base
subtle
border
emphasis
on
secondary
base
subtle
border
emphasis
on
success
base
subtle
border
emphasis
on
danger
base
subtle
border
emphasis
on
warning
base
subtle
border
emphasis
on
info
base
subtle
border
emphasis
on
neutral
base
subtle
border
emphasis
on
light
base
subtle
border
emphasis
on
dark
base
subtle
border
emphasis
on
Alert primary
Alert secondary
Alert success
Alert danger
Alert warning
Alert info
Alert neutral
Alert light
Alert dark

Build your theme · change the base colours only

Define only the base colour of each semantic family. The rest (subtle, border-subtle, emphasis, on) is derived automatically with color-mix / relative color — and the components below re-theme live, which is the shared wiring in action.

primary base → derived
base
subtle
border
emphasis
on
secondary base → derived
base
subtle
border
emphasis
on
success base → derived
base
subtle
border
emphasis
on
danger base → derived
base
subtle
border
emphasis
on
warning base → derived
base
subtle
border
emphasis
on
info base → derived
base
subtle
border
emphasis
on
neutral base → derived
base
subtle
border
emphasis
on
light base → derived
base
subtle
border
emphasis
on
dark base → derived
base
subtle
border
emphasis
on

Live components

Alert primary
Alert secondary
Alert success
Alert danger
Alert warning
Alert info
Alert neutral
Alert light
Alert dark
primarysecondarysuccessdangerwarninginfoneutrallightdark
.my-theme {
  /* define ONLY the base colour of each family */
  --hub-sys-color-primary: #6d28d9;

  /* the rest derives at runtime with color-mix / relative color — nothing to maintain */
  --hub-sys-color-primary-subtle:        color-mix(in oklch, var(--hub-sys-color-primary) 12%, var(--hub-sys-surface-page));
  --hub-sys-color-primary-border-subtle: color-mix(in oklch, var(--hub-sys-color-primary) 35%, var(--hub-sys-surface-page));
  --hub-sys-color-primary-emphasis:      color-mix(in oklch, var(--hub-sys-color-primary) 80%, var(--hub-sys-color-ink));
  --hub-sys-color-primary-on:            oklch(from var(--hub-sys-color-primary) clamp(0, (0.62 - l) * 1000, 1) 0 h);
}

The family · integration status

The whole family reads these tokens. The badge says whether a library is already wired to --hub-sys-* (it re-themes with the design system) or is still pending (we get to it later). Click any of them to see its examples.

How to modify it

1. Override a token (CSS) — the main road

Every value is a CSS variable, so re-theming is one line and cascades to the whole family:

:root {
  --hub-sys-color-primary: #7c3aed;
  --hub-sys-color-primary-subtle: #ede9fe;
  --hub-sys-color-primary-emphasis: #5b21b6;
}

2. Add an accent of your own

Define a --hub-sys-color-<name> family and the components that accept a semantic variant pick it up unchanged. Try it live here:

brand alert — background, text and border derived from the accent with color-mix.
:root {
  --hub-sys-color-brand: #9333ea;
  --hub-sys-color-brand-subtle: #f3e8ff;
  --hub-sys-color-brand-border-subtle: #d8b4fe;
  --hub-sys-color-brand-emphasis: #6b21a8;
}

<!-- in any component that accepts a semantic variant -->
<hub-panel appearance="alert" variant="brand">Brand alert</hub-panel>

--hub-sys-color-brand on its own already works (the components derive the rest with color-mix). Define the full family when you want exact tints.

… or predefine it in the open map (Sass)

If you use Sass, extend the open accent map before including the package, with $hub-accents-extra. Only ds is recompiled — never the libraries — and every new variant gets its full family (subtle · border-subtle · emphasis · on):

// Add your own variants — this recompiles the ds only, never the libraries.
@use 'ng-hub-ui-ds/styles/tokens/hub-tokens' with (
  $hub-accents-extra: (
    brand:    #ff6b00,
    accent:   #00b8d9,
    tertiary: #9c36b5
  )
);
// brand · accent · tertiary get subtle · border-subtle · emphasis · on automatically.

3. Create a theme of your own

[data-theme='corporate'] {
  --hub-sys-color-primary: #0033a0;
  --hub-sys-surface-page: #fbfcff;
  --hub-sys-text-primary: #0a1f44;
  /* …the rest of the tokens that differ from the base */
}

4. …or all of it at once with hub.theme() (Sass)

A single partial theming mixin: hand it only the changes as maps and it emits the variables under their canonical names. Everything hanging off each token — sys aliases, role families, utilities, components — re-derives on its own along the var() chain:

@use 'ng-hub-ui-ds' as hub;

/* change ONLY spacing step 3 — gap-3, .p-3, sys-gap-3, components… all move with it */
:root { @include hub.theme($space: (3: 1.25rem)); }

/* or a whole theme, scopeable to [data-theme] or to a subtree */
[data-theme='compact'] {
  @include hub.theme(
    $space: (3: 0.75rem, 4: 1rem),
    $radius: (md: 0.25rem),
    $accents: (primary: #0f766e, brand: #ff6b00)
  );
}

SCSS functions: how it is generated inside

The semantic colour families are not written by hand: the accent set is an open map with !default and the family is derived with an @each over map.keys($hub-accents). That is why adding one colour (or nine) is uniform and free of boilerplate: every key in the map gets its family automatically.

// The accent set is an OPEN map with !default — add as many as you want.
$hub-accents: (
  primary: …, secondary: …, success: …, danger: …, warning: …,
  info: …, neutral: …, light: …, dark: …
) !default;

// The family is derived ONCE in :root, iterating EVERY key of the map,
// so any accent you add gets its family for free.
@mixin hub-color-derive() {
  @each $name in map.keys($hub-accents) {
    --hub-sys-color-#{$name}-subtle:   color-mix(in oklch, var(--hub-sys-color-#{$name}) 12%, var(--hub-sys-surface-page));
    --hub-sys-color-#{$name}-emphasis: color-mix(in oklch, var(--hub-sys-color-#{$name}) 80%, var(--hub-sys-color-ink));
    --hub-sys-color-#{$name}-on:       oklch(from var(--hub-sys-color-#{$name}) clamp(0, (0.62 - l) * 1000, 1) 0 h);
  }
}

For most cases you do not need to touch the SCSS: overriding CSS variables covers re-theming and adding accents. The map plus mixin is the internal machinery, useful if you contribute to the package.

Structural layer · live tokens

Beyond colour, ds defines a canonical structural layer: the spacing scale (--hub-ref-space-*), sizing and fractions (--hub-sys-size-*), grid (--hub-sys-grid-columns, gutters) and a semantic gap scale (--hub-sys-gap-*). Everything below is painted by consuming those variables.

Spacing scale · --hub-ref-space-0…7

space-0
space-1
space-2
space-3
space-4
space-5
space-6
space-7

Gap scale · --hub-sys-gap-0…5

gap-0
ABC
gap-1
ABC
gap-2
ABC
gap-3
ABC
gap-4
ABC
gap-5
ABC

Width fractions · --hub-sys-size-*

size-1-21/2
size-1-31/3
size-2-32/3
size-1-41/4
size-3-43/4
size-fullfull

Grid auto-fit + container widths

col 1col 2col 3col 4col 5col 6
max-sm
max-md
max-lg
max-xl
max-xxl
/* Canonical structural tokens (ref → sys) */
gap: var(--hub-sys-gap-3);                  /* spacing scale */
padding-inline: var(--hub-ref-space-4);
max-width: var(--hub-sys-container-max-width-lg);
grid-template-columns: repeat(var(--hub-sys-grid-columns), minmax(0, 1fr));

/* Layout mixins */
@use 'ng-hub-ui-ds' as hub;
.page    { @include hub.center($max: xl); }
.toolbar { @include hub.cluster($gap: 2); }
.cards   { @include hub.grid($min: 16rem, $gap: 3); }

/* Opt-in utilities (Bootstrap naming, 3 sheets) */
@use 'ng-hub-ui-ds/styles/utilities/layout';    // .d-flex .gap-3 .ms-auto .position-* .ratio-*
@use 'ng-hub-ui-ds/styles/utilities/text';      // .fs-3 .fw-bold .text-truncate .text-muted
@use 'ng-hub-ui-ds/styles/utilities/surfaces';  // .bg-* .border .rounded .shadow .opacity-*

Mixins and styles

The ds Sass API: helpers that consume the canonical tokens — with no spacing or colour baked in. They are imported namespaced, with @use 'ng-hub-ui-ds' as hub. The utility sheets are built from these same mixins, so changing or extending a behaviour means minting your own class with the mixin. Every demo below is built from the real mixin; the full catalogue is at the end.

Every mixin has an equivalent utility; the demos below consume the real ds class (.cluster, .stack…) — this page re-applies no mixin.

hub.cluster() · .cluster — a row that wraps

HomeProductsPricingContactMore

hub.stack() · .stack — a column

Block 1Block 2Block 3

hub.grid() · .grid-auto — auto-fit grid

12345678

hub.center() · .center — centred wrapper

centred content (max-width xl)
@use 'ng-hub-ui-ds' as hub;

.toolbar { @include hub.cluster($gap: 2); }          // a row that wraps
.feed    { @include hub.stack($gap: 3); }            // a column
.cards   { @include hub.grid($min: 8rem, $gap: 2); } // auto-fit grid
.page    { @include hub.center($max: sm); }          // centred wrapper

Full catalogue

MixinParametersDescription
Theme
hub.theme()$accents, $space, $gap, $radius, $shadow, $font-*, $line-height, $tokensPartial theming in one call: hand it only the changes as maps and it emits the canonical variables; whatever derives from them re-derives itself.
Breakpoints
hub.media-breakpoint-up()$name, $breakpoints: $hub-breakpointsMobile-first media query from the breakpoint up (sm 576 · md 768 · lg 992 · xl 1200 · xxl 1400).
hub.media-breakpoint-down()$name, $breakpoints: $hub-breakpointsMedia query below the breakpoint.
Layout
hub.stack()$gap: 3, $align: stretchFlex column with a token gap.
hub.cluster()$gap: 2, $align: center, $justify: flex-startFlex row that wraps.
hub.grid()$min: 16rem, $gap: 4Auto-fit grid with a minimum width per cell.
hub.grid-fixed()$cols: 12, $gap: 4Grid of N fixed columns.
hub.row()$gap: 3Row of 12 tracks (--hub-sys-grid-columns).
hub.col()$span: 12Cell spanning $span tracks of a row().
hub.offset()$n: 0Starts a col() on track $n + 1 (Bootstrap's offset); 0 resets it.
hub.center()$max: xl, $pad: 3Wrapper centred to a container max-width.
Typography
hub.font-family()$family: basebase | display | mono.
hub.font-size()$size: base1–6 (heading scale) or xs | sm | base | lg.
hub.font-weight()$weight: baselight | base | medium | semibold | bold.
hub.line-height()$height: base1 | sm | base | lg.
hub.text-color()$variant: bodyAn accent from the open map, body or muted; dimmed with --hub-text-opacity.
hub.link-color()$variant: primaryAccent link with an -emphasis hover and an underline with opacity (--hub-link-underline-opacity).
hub.text-truncate()—Single-line truncation with an ellipsis.
hub.text-break()—Breaks long words and URLs instead of overflowing.
Surfaces
hub.bg()$variant: body, $subtle: falseAccent background (or its subtle tint), body or transparent; dimmed with --hub-bg-opacity.
hub.text-bg()$variant: primaryAccent background + the -on colour with guaranteed contrast.
hub.border()$width: 1px, $color: …Solid border with the default border token.
hub.border-color()$variant: primaryBorder colour from an accent of the open map.
hub.radius()$size: mdnone | sm | md | lg | xl | xxl | pill | circle.
hub.shadow()$size: mdnone | sm | md | lg | inset.
Helpers
hub.focus-ring()—Focus ring using the --hub-sys-focus-ring-* tokens.
hub.visually-hidden()—Hides it visually while keeping it available to screen readers.
hub.stretched-link()—Extends the link's clickable area to its positioned ancestor.
hub.ratio()$x: 16, $y: 9Fixed proportion with native aspect-ratio.
hub.clearfix()—Clears floats (::after).
Bridges
hub.bridge-bootstrap()$mode: adopt | projectAdopts the --bs-* theme or projects onto it.
hub.bridge-material()$mode: adopt | projectThe same with --mat-sys-* (Angular Material 3).
hub.bridge-tailwind()$mode: adopt | projectThe same with --color-* (Tailwind v4 @theme).
hub.bridge-open-props()$mode: adoptAliases of the Open Props ramps.

Utilities

Opt-in classes wrapping the same layer, under names exactly matching Bootstrap's — a drop-in replacement, but do not load these sheets AND Bootstrap globally in the same document. Three sheets: layout (display/flex, grid, spacing, sizing, position, helpers — these sections), text (the “Text” section) and surfaces (the “Surfaces” section). The demo uses the real classes:

.cluster .gap-2

onetwothreefour

.grid-auto .gap-2

ABCDEF

Sizing · .w-50 · .w-25 · .w-auto

w-50w-25w-auto
// three opt-in sheets — import only the ones you use
@use 'ng-hub-ui-ds/styles/utilities/layout';    // layout, spacing, position, helpers
@use 'ng-hub-ui-ds/styles/utilities/text';      // typography and text
@use 'ng-hub-ui-ds/styles/utilities/surfaces';  // backgrounds, borders, radii, shadows

<div class="cluster gap-2"> … </div>
<div class="grid-auto gap-2"> … </div>
<div class="w-50">half</div>
<div class="vh-100 min-vh-100">viewport height</div>

Flexbox

A complete set of display and flexbox utilities, under Bootstrap's exact naming. The demos use the real ds classes.

justify-content · .justify-content-* — main axis

.justify-content-start
ABC
.justify-content-end
ABC
.justify-content-center
ABC
.justify-content-between
ABC
.justify-content-around
ABC
.justify-content-evenly
ABC

align-items · .align-items-* — cross axis

.align-items-start
smmdlg
.align-items-center
smmdlg
.align-items-end
smmdlg
.align-items-stretch
smmdlg
.align-items-baseline
smmdlg

flex-direction · .flex-column

row 1row 2row 3

flex-fill / flex-grow-0 — grow and pin

.flex-fill (grows).flex-grow-0

Responsive · .flex-md-row · .align-items-md-center

The whole flex family has per-breakpoint variants. A column on mobile, a centred row from md up — resize the window:

onetwothree

Vertical divider · .vr

leftright
<!-- display + direction / wrap -->
<div class="d-flex flex-wrap gap-2"> … </div>
<div class="d-flex flex-column"> … </div>

<!-- main axis (justify) and cross axis (align) -->
<div class="d-flex justify-content-between align-items-center"> … </div>

<!-- grow / pin -->
<div class="d-flex gap-2">
  <span class="flex-fill">grows</span>
  <span class="flex-grow-0">fixed</span>
</div>

<!-- responsive: a column on mobile, a top-aligned row from md up -->
<div class="d-flex flex-column flex-md-row align-items-md-start"> … </div>

<!-- vertical divider -->
<div class="d-flex gap-3">left <span class="vr"></span> right</div>

Columns, widths and spacing

A 12-column grid (.row + .col-1…12 + .offset-1…11, driven by --hub-sys-grid-columns), widths and directional padding/margin. Every demo uses the real utilities.

Columns · .row + .col-N

col-6
col-6
col-4
col-4
col-4
col-3
col-3
col-6
col-8
col-4
col-12
col-2
col-2
col-2
col-2
col-2
col-2

Offsets · .offset-N

The cell starts on track n + 1 of the same grid (grid-column-start); with a breakpoint, .offset-md-8, and .offset-md-0 resets it to automatic placement.

col-4 offset-4
col-3 offset-9

Widths · .w-25 / 50 / 75 / 100 / auto

w-25w-25
w-50w-50
w-75w-75
w-100w-100
w-autow-auto

Padding · .p-1…5 (all four sides)

p-1
p-2
p-3
p-4
p-5

Padding per axis · .px-* / .py-*

px-4 · py-1
px-2 · py-4
px-5 · py-3

Margin · .m-1…5 (inside a reference frame)

m-1
m-2
m-3
m-4
m-5

Margin per axis · .mx-* / .my-*

mx-4
my-3

Responsive · .col-md-6 · .d-lg-none · .p-sm-3

Mobile-first variants (sm 576 · md 768 · lg 992 · xl 1200 · xxl 1400) for display, columns and offsets, spacing, gap and the whole flex family (.flex-md-row, .justify-content-md-center, .align-items-md-start, .order-lg-*…). Resize the window — these cells are .col-12 .col-md-6 .col-xl-3:

col-12 · md-6 · xl-3
col-12 · md-6 · xl-3
col-12 · md-6 · xl-3
col-12 · md-6 · xl-3
<!-- mobile-first: 1 column → 2 at ≥768px → 4 at ≥1200px -->
<div class="row">
  <div class="col-12 col-md-6 col-xl-3">…</div>
</div>
<div class="d-none d-lg-flex">desktop only</div>
<section class="p-2 p-md-4">padding that grows with the viewport</section>

// breakpoints as a mixin — for any other utility or your own CSS
@use 'ng-hub-ui-ds' as hub;
@include hub.media-breakpoint-up(md) { .sidebar { width: 18rem; } }
<!-- 12-column grid: every row adds up to 12 -->
<div class="row">
  <div class="col-6">col-6</div>
  <div class="col-6">col-6</div>
</div>
<div class="row">
  <div class="col-4">col-4</div>
  <div class="col-4">col-4</div>
  <div class="col-4">col-4</div>
</div>

<!-- offset: .col-4.offset-8 starts on track 9 (offset-{bp}-0 resets it) -->
<div class="row">
  <div class="col-4 offset-md-8">col-4 offset-md-8</div>
</div>

<!-- widths · directional padding and margin -->
<div class="w-50">w-50</div>
<div class="p-3">1rem of padding on all four sides</div>
<div class="px-4 py-2">padding-inline 1.5rem · padding-block .5rem</div>
<div class="mx-3">margin-inline 1rem</div>

Text

Typography and text utilities under Bootstrap's exact naming, built on the typography mixins (hub.font-size(), hub.font-weight(), hub.text-color()…): every value resolves to tokens, so overriding the token re-themes the utilities — and with the mixins you can mint variants of your own.

Size · .fs-1…6 · headings · .h1…6

.fs-1Sample headline
.fs-2Sample headline
.fs-3Sample headline
.fs-4Sample headline
.fs-5Sample headline
.fs-6Sample headline

.fs-* changes the size only; .h1…6 applies a heading's full typography (size + bold + tight leading + margin) without the semantics — a <div class="h5"> reads like an <h5>:

.h1
Sample headline
.h2
Sample headline
.h3
Sample headline
.h4
Sample headline
.h5
Sample headline
.h6
Sample headline

Weight · .fw-* · style · .fst-italic

.fw-lightThe weight comes from the token — light
.fw-normalThe weight comes from the token — normal
.fw-mediumThe weight comes from the token — medium
.fw-semiboldThe weight comes from the token — semibold
.fw-boldThe weight comes from the token — bold
.fst-italicitalic

Alignment · .text-start / .text-center / .text-end

.text-start
.text-center
.text-end

Transform · decoration · family

.text-uppercase.text-capitalize.text-decoration-underline.text-decoration-line-through.font-monospace

Tabular figures · .font-tabular-nums

Proportional figures give every digit its own width, so a 1 is narrower than a 0 and the units of two amounts land in different places. A column of money is read by comparing downwards: look at where the decimal separator falls on each side.

Default
1.111,11 €
9.087,70 €
11,10 €
777.777,77 €
.font-tabular-nums
1.111,11 €
9.087,70 €
11,10 €
777.777,77 €

Truncation · .text-truncate

A text long enough not to fit into sixteen rem and to be truncated with an ellipsis.

Leading · .lh-1 / .lh-sm / .lh-base / .lh-lg

.lh-1Two lines of text, enough to judge the leading each step of the scale applies.
.lh-smTwo lines of text, enough to judge the leading each step of the scale applies.
.lh-baseTwo lines of text, enough to judge the leading each step of the scale applies.
.lh-lgTwo lines of text, enough to judge the leading each step of the scale applies.

Colours · .text-primary … .text-muted

primarysecondarysuccessdangerwarninginfoneutrallightdarkbodymuted

Text opacity · .text-opacity-* · links · .link-*

255075100
// import the sheet once
@use 'ng-hub-ui-ds/styles/utilities/text';

<h2 class="fs-3 fw-semibold">Headline</h2>
<p class="text-muted lh-lg">Secondary copy with generous leading</p>
<span class="text-uppercase fw-medium">label</span>
<td class="text-end font-monospace">1.234,56 €</td>
<p class="text-truncate" style="max-width: 16rem">Truncated with an ellipsis…</p>

// …or mint variants of your own with the mixins
@use 'ng-hub-ui-ds' as hub;
.card__title { @include hub.font-size(4); @include hub.font-weight(semibold); }
.card__meta  { @include hub.text-color(muted); @include hub.text-truncate(); }

Surfaces

Backgrounds, borders, radii, shadows and opacity under Bootstrap's exact naming, built on the surface mixins (hub.bg(), hub.text-bg(), hub.radius(), hub.shadow()…) and the --hub-sys-color-* / --hub-sys-radius-* / --hub-sys-shadow-* tokens.

Backgrounds · .bg-* and .bg-*-subtle

Background + guaranteed contrast · .text-bg-*

primarysecondarysuccessdangerwarninginfoneutrallightdark

Borders · .border · .border-{variant} · .border-1…5

borderprimarydanger · 3no top/bottom

Radii · .rounded-0…3 · .rounded-pill · .rounded-circle

012345pill1:1

Shadows · .shadow-sm / .shadow / .shadow-lg

smmdlg

Opacity · .opacity-* (element) · .bg-opacity-* (background only)

255075100
bg-10bg-25bg-50bg-75bg-100
// import the sheet once
@use 'ng-hub-ui-ds/styles/utilities/surfaces';

<hub-badge color="success">active</hub-badge>
<div class="bg-warning-subtle border border-warning rounded-3 p-3">notice</div>
<div class="bg-body rounded shadow-lg p-4">raised card</div>

// …or mint variants of your own with the mixins
@use 'ng-hub-ui-ds' as hub;
.card        { @include hub.bg(body); @include hub.radius(lg); @include hub.shadow(sm); }
.card--brand { @include hub.text-bg(brand); }  // any accent of the open map

Position and helpers

Position/inset, overflow, order, float, visibility, z-index, object-fit and interactions — plus the behavioural helpers (.visually-hidden, .stretched-link, .ratio-*, .clearfix). All under Bootstrap's exact naming.

Position · .position-* + .top/.start/.translate-middle

Ratio · .ratio .ratio-16x9

1x1
4x3
16x9
21x9

Overflow · interaction · lateral spacing

A block holding more content than fits: overflow-auto adds an inner scrollbar instead of spilling over the page layout.
user-select-nonepe-nonems-auto

Focus ring · .focus-ring

A focus ring driven by the --hub-sys-focus-ring-* tokens — click it or press Tab:

Accessibility · .visually-hidden

This paragraph holds a text hidden visually but announced by screen readers: for assistive technology only (inspect the DOM to see it).

<!-- badge positioned over the corner of its container -->
<div class="position-relative">
  …
  <span class="position-absolute top-0 start-100 translate-middle text-bg-danger rounded-pill px-2">9+</span>
</div>

<!-- media at a fixed ratio -->
<div class="ratio ratio-16x9"><iframe src="…"></iframe></div>

<!-- whole card clickable + screen-reader-only text -->
<div class="position-relative">
  <a href="…" class="stretched-link">See more<span class="visually-hidden"> about surfaces</span></a>
</div>

<!-- push with auto margins inside a flex row -->
<nav class="d-flex gap-2"><span>logo</span><a class="ms-auto">login</a></nav>

Bridges to other systems

Opt-in mixins that connect hub with whichever framework is present. Zero cost if you never call them. Two directions through $mode: adopt (hub takes the host's theme) and project (hub projects its theme onto the host).

@use 'ng-hub-ui-ds' as hub;

/* "I have Bootstrap and I want hub to adopt its palette" */
:root { @include hub.bridge-bootstrap($mode: adopt); }

/* "I want to tint Material and Tailwind with MY hub theme" */
:root { @include hub.bridge-material($mode: project); }
:root { @include hub.bridge-tailwind($mode: project); }
MixinSystemadoptproject
hub.bridge-bootstrapBootstrap 5.3+reads --bs-*tints --bs-* + gutters/radius
hub.bridge-materialAngular Material 3reads --mat-sys-*tints --mat-sys-* (colour/on pair)
hub.bridge-tailwindTailwind v4reads --color-*emits --color-* for @theme
hub.bridge-open-propsOpen Propsramp aliases —

Quick reference

I want to…How
Use the palette@use '…/hub-tokens'
Change a global colour:root { --hub-sys-color-primary: … }
Add variants of my ownwith ($hub-accents-extra: (brand: …))
Text with contrastcolor: var(--hub-sys-color-primary-on)
Layout (mixin / utility)@include hub.center() · .stack
Text (mixin / utility)@include hub.font-size(3) · .fs-3 .fw-bold .text-muted
Surfaces (mixin / utility)@include hub.text-bg(brand) · .bg-primary-subtle .rounded .shadow
Position / helpers.position-absolute .top-0 · .ratio-16x9 · .visually-hidden
Responsive.col-md-6 · .flex-md-row · .offset-md-8 · @include hub.media-breakpoint-up(md)
Bridge to Bootstrap/Material@include hub.bridge-bootstrap($mode: adopt)
Create a theme[data-theme='<name>'] { … }
Partial theming (Sass)@include hub.theme($space: (3: 1.25rem), $accents: (primary: …))