Customization

@alviere/ui components are styled through a layered customization system. There are four approaches, from broadest to most targeted:

Approach Scope Use when
CSS custom properties (design tokens) Global or scoped Rebranding, theming, consistent style changes across all components
Theme presets Global or scoped Switching between pre-built palettes (dark, brand-ocean, etc.)
CSS ::part() Single component element Targeting a specific internal element that no token covers
class prop / color props Single component instance One-off overrides on Button, Spinner, or Timeline

CSS design tokens

How tokens are applied

Design tokens are automatically injected into :root when the first SDK component loads. You do not need to import any CSS file. The full token set is available as soon as any component is present on the page.

To verify injection, inspect :root in your browser's DevTools — you'll see all --alv-* variables defined there.

Overriding tokens

Redefine any token in your own stylesheet. Because the SDK injects into :root, your overrides just need to have equal or higher specificity:

/* Apply globally */
:root {
  --alv-color-primary: #0066cc;
  --alv-color-primary-hover: #0052a3;
  --alv-font-family-sans: "Inter", sans-serif;
}

/* Scope to a specific container */
.payment-widget {
  --alv-color-primary: #7c3aed;
  --alv-input-height: 2.75rem;
}

Scoped overrides cascade into all SDK components rendered inside that container — useful for embedding the SDK in a section of a larger page without affecting the rest of your styles.

CSS Layers

Token styles are injected inside @layer alviere-defaults. If your application uses @layer, your override layer must be declared after alviere-defaults to win the cascade:

@layer alviere-defaults, my-overrides;

@layer my-overrides {
  :root {
    --alv-color-primary: #0066cc;
  }
}

If you don't use @layer, unlayered rules always win over layered ones — your plain :root overrides will apply automatically.

Pre-load configuration

To set tokens before any component renders (e.g. to avoid a flash of the default brand color), configure via window.ALVIERE_TOKENS_CONFIG before the SDK script loads:

<script>
  window.ALVIERE_TOKENS_CONFIG = {
    customTokens: {
      '--alv-color-primary': '#0066cc',
      '--alv-color-primary-hover': '#0052a3'
    }
  };
</script>
<script src="web-components.js"></script>

To opt out of auto-injection entirely and manage the stylesheet yourself:

<script>
  window.ALVIERE_TOKENS_CONFIG = { disableAutoInject: true };
</script>

Programmatic API

import { configureTokens, ensureTokensInjected } from '@alviere/ui';

configureTokens({
  customTokens: {
    '--alv-color-primary': '#0066cc'
  }
});

// Force injection if you disabled auto-inject
ensureTokensInjected();

Shadow DOM

If you're embedding SDK components inside your own custom element (Shadow DOM), call adoptTokensInShadowRoot to make the tokens available inside your shadow root:

import { adoptTokensInShadowRoot } from '@alviere/ui';

class MyWidget extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({ mode: 'open' });
    adoptTokensInShadowRoot(shadow);
    // SDK components rendered inside shadow now have access to all tokens
  }
}

Theme presets

Five built-in theme presets are available:

Preset data-theme value
Light (default) light or omit attribute
Dark dark
Ocean brand brand-ocean
Forest brand brand-forest
Sunset brand brand-sunset

Apply a preset by setting the data-theme attribute on any ancestor element, including :root:

<!-- Dark mode for the entire page -->
<html data-theme="dark">

<!-- Scoped to a container -->
<div data-theme="brand-ocean">
  <alviere-create-account ...></alviere-create-account>
</div>

Programmatic theming

import { ThemeManager } from '@alviere/ui';

const theme = new ThemeManager();

// Apply to document root
theme.setTheme('dark');

// Apply to a specific element
theme.setTheme('brand-ocean', document.querySelector('#payment-section'));

CSS ::part()

Three components expose named CSS parts for styling internal elements that tokens don't reach. Parts are targeted with the ::part() pseudo-element.

Button

alviere-button::part(root)  { /* the <button> element */ }
alviere-button::part(icon)  { /* icon container */ }
alviere-button::part(label) { /* label/text content */ }

Spinner

alviere-spinner::part(root)        { /* root container */ }

/* Circular variant */
alviere-spinner::part(circle)      { /* SVG circle element */ }
alviere-spinner::part(circle-path) { /* circle stroke path */ }

/* Dots variant */
alviere-spinner::part(dots) { /* dots container */ }
alviere-spinner::part(dot)  { /* individual dot */ }

Timeline

alviere-timeline::part(root)             { /* root container */ }
alviere-timeline::part(container)        { /* steps wrapper */ }
alviere-timeline::part(step-wrapper)     { /* per-step wrapper */ }
alviere-timeline::part(connector)        { /* connector line between steps */ }
alviere-timeline::part(step)             { /* individual step */ }
alviere-timeline::part(step-indicator)   { /* step circle/badge */ }
alviere-timeline::part(step-icon)        { /* icon inside the indicator */ }
alviere-timeline::part(step-ripple)      { /* ripple animation element */ }
alviere-timeline::part(step-label)       { /* label container */ }
alviere-timeline::part(step-text)        { /* label text */ }
alviere-timeline::part(step-description) { /* optional description text */ }

CSS parts are not exposed on input components, form components, or most element components. Use CSS custom properties or scoped overrides for those.


class prop

Button, Spinner, and Timeline accept a class prop for applying additional CSS classes to their root element. Other components do not support this prop.

<Button class="my-submit-btn" variant="primary">Submit</Button>
<alviere-button class="my-submit-btn">Submit</alviere-button>

Button and Spinner also accept color, primaryColor, and backgroundColor props that apply inline CSS variable overrides to a single instance without touching the global token sheet — useful for one-off color variations.


Token reference

Colors

Primary

Token Default
--alv-color-primary #227e9e
--alv-color-primary-hover #1b6680
--alv-color-primary-active #145164
--alv-color-primary-light #e8f4f8
--alv-color-primary-light-hover #d1e8f0
--alv-color-on-primary #ffffff

Success

Token Default
--alv-color-success #436b1d
--alv-color-success-light #d4edda
--alv-color-success-border #c3e6cb
--alv-color-success-text #155724
--alv-color-on-success #ffffff
--alv-color-success-rgb 67, 107, 29

Error

Token Default
--alv-color-error #b3311f
--alv-color-error-light #f8d7da
--alv-color-error-border #f5c6cb
--alv-color-error-text #721c24
--alv-color-on-error #ffffff
--alv-color-error-rgb 179, 49, 31

Warning

Token Default
--alv-color-warning #ffc107
--alv-color-warning-light #fff3cd
--alv-color-warning-border #ffeaa7
--alv-color-warning-text #856404
--alv-color-on-warning #000000

Info

Token Default
--alv-color-info #175db8
--alv-color-info-light #d1ecf1
--alv-color-info-border #bee5eb
--alv-color-info-text #0c5460
--alv-color-on-info #ffffff

Gray scale

Token Default
--alv-color-gray-50 #fafafa
--alv-color-gray-100 #f4f4f5
--alv-color-gray-200 #e4e4e7
--alv-color-gray-300 #d4d4d8
--alv-color-gray-400 #a1a1aa
--alv-color-gray-500 #71717a
--alv-color-gray-600 #52525b
--alv-color-gray-700 #3f3f46
--alv-color-gray-800 #27272a
--alv-color-gray-900 #18181b

Surface

Token Default
--alv-color-surface #ffffff
--alv-color-surface-secondary #f9fafb
--alv-color-surface-tertiary #f4f4f5
--alv-color-surface-elevated #ffffff

Text

Token Default
--alv-color-text-primary #18181b
--alv-color-text-secondary #52525b
--alv-color-text-tertiary #71717a
--alv-color-text-disabled #a1a1aa
--alv-color-text-on-primary #ffffff
--alv-color-text-on-surface #18181b

Border

Token Default
--alv-color-border #e4e4e7
--alv-color-border-hover #d4d4d8
--alv-color-border-focus #227e9e
--alv-color-border-error #b3311f
--alv-color-border-success #436b1d

Background

Token Default
--alv-color-background #ffffff
--alv-color-background-disabled #f4f4f5
--alv-color-background-hover #f9fafb
--alv-color-background-active #f4f4f5

Typography

Font families

--alv-font-family-sans:      -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--alv-font-family-grotesque: "Darker Grotesque", sans-serif;
--alv-font-family-mono:      "Monaco", "Menlo", "Ubuntu Mono", monospace;

Font sizes

--alv-font-size-3xs:  0.625rem;  /* 10px */
--alv-font-size-2xs:  0.75rem;   /* 12px */
--alv-font-size-xs:   1rem;      /* 16px */
--alv-font-size-sm:   1.125rem;  /* 18px */
--alv-font-size-base: 1.25rem;   /* 20px */
--alv-font-size-lg:   1.5rem;    /* 24px */
--alv-font-size-xl:   1.75rem;   /* 28px */

Font weights

--alv-font-weight-light:    300;
--alv-font-weight-normal:   400;
--alv-font-weight-medium:   500;
--alv-font-weight-semibold: 600;
--alv-font-weight-bold:     700;

Line heights

--alv-line-height-tight:   1.25;
--alv-line-height-normal:  1.5;
--alv-line-height-relaxed: 1.75;

Spacing

--alv-spacing-xs:  0.25rem;  /*  4px */
--alv-spacing-sm:  0.5rem;   /*  8px */
--alv-spacing-md:  1rem;     /* 16px */
--alv-spacing-lg:  1.5rem;   /* 24px */
--alv-spacing-xl:  2rem;     /* 32px */
--alv-spacing-2xl: 3rem;     /* 48px */
--alv-spacing-3xl: 4rem;     /* 64px */

Border radius

--alv-border-radius-none: 0;
--alv-border-radius-sm:   0.125rem;  /*  2px */
--alv-border-radius:      0.25rem;   /*  4px — default */
--alv-border-radius-md:   0.375rem;  /*  6px */
--alv-border-radius-lg:   0.5rem;    /*  8px */
--alv-border-radius-xl:   0.75rem;   /* 12px */
--alv-border-radius-2xl:  1rem;      /* 16px */
--alv-border-radius-full: 9999px;

Shadows

--alv-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--alv-shadow:    0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
--alv-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--alv-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
--alv-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
--alv-shadow-none: none;

Transitions

--alv-transition-fast:   150ms ease-in-out;
--alv-transition-normal: 250ms ease-in-out;
--alv-transition-slow:   350ms ease-in-out;
--alv-transition-none:   none;

Z-index

--alv-z-dropdown:       1000;
--alv-z-sticky:         1020;
--alv-z-fixed:          1030;
--alv-z-modal-backdrop: 1040;
--alv-z-modal:          1050;
--alv-z-popover:        1060;
--alv-z-tooltip:        1070;

Component tokens

Input fields

--alv-input-height-sm:      2.5rem;
--alv-input-height:         3.25rem;
--alv-input-height-lg:      3.75rem;
--alv-input-padding-x:      0.75rem;
--alv-input-padding-y:      0.5rem;
--alv-input-border-color:   var(--alv-color-border);
--alv-input-border-hover:   var(--alv-color-border-hover);
--alv-input-border-focus:   var(--alv-color-border-focus);
--alv-input-bg:             var(--alv-color-background);
--alv-input-bg-disabled:    var(--alv-color-background-disabled);

Button

/* Primary */
--alv-button-primary-bg:        var(--alv-color-primary);
--alv-button-primary-bg-hover:  var(--alv-color-primary-hover);
--alv-button-primary-bg-active: var(--alv-color-primary-active);
--alv-button-primary-text:      var(--alv-color-on-primary);
--alv-button-primary-border:    var(--alv-color-primary);

/* Secondary */
--alv-button-secondary-bg:        transparent;
--alv-button-secondary-bg-hover:  var(--alv-color-primary-light);
--alv-button-secondary-bg-active: var(--alv-color-primary-light-hover);
--alv-button-secondary-text:      var(--alv-color-primary);
--alv-button-secondary-border:    var(--alv-color-primary);

/* Tertiary */
--alv-button-tertiary-bg:        transparent;
--alv-button-tertiary-bg-hover:  var(--alv-color-gray-100);
--alv-button-tertiary-bg-active: var(--alv-color-gray-200);
--alv-button-tertiary-text:      var(--alv-color-text-primary);
--alv-button-tertiary-border:    transparent;

/* Link */
--alv-button-link-bg:         transparent;
--alv-button-link-bg-hover:   transparent;
--alv-button-link-bg-active:  transparent;
--alv-button-link-text:       var(--alv-color-primary);
--alv-button-link-text-hover: var(--alv-color-primary-hover);
--alv-button-link-border:     transparent;

Spinner

--alv-spinner-color:        var(--alv-color-text-primary);
--alv-spinner-size-sm:      1rem;
--alv-spinner-size-md:      1.5rem;
--alv-spinner-size-lg:      4rem;
--alv-spinner-stroke-width: 2px;

Timeline

--alv-timeline-color:           var(--alv-color-gray-600);
--alv-timeline-completed-color: var(--alv-color-success);
--alv-timeline-error-color:     var(--alv-color-error);
--alv-timeline-pending-color:   var(--alv-color-gray-400);
--alv-timeline-step-size-sm:    1.25rem;
--alv-timeline-step-size-md:    2rem;
--alv-timeline-step-size-lg:    2.5rem;
--alv-timeline-connector-width: 3px;
--alv-timeline-font-size:       var(--alv-font-size-base);

Badge

--alv-badge-primary-bg:    var(--alv-color-primary);
--alv-badge-primary-text:  var(--alv-color-on-primary);
--alv-badge-secondary-bg:  var(--alv-color-gray-200);
--alv-badge-secondary-text:var(--alv-color-text-primary);
--alv-badge-success-bg:    var(--alv-color-success);
--alv-badge-success-text:  var(--alv-color-on-success);
--alv-badge-error-bg:      var(--alv-color-error);
--alv-badge-error-text:    var(--alv-color-on-error);
--alv-badge-warning-bg:    var(--alv-color-warning);
--alv-badge-warning-text:  var(--alv-color-on-warning);
--alv-badge-info-bg:       var(--alv-color-info);
--alv-badge-info-text:     var(--alv-color-on-info);

List

--alv-list-bg:                  var(--alv-color-surface);
--alv-list-border:              var(--alv-color-border);
--alv-list-item-bg:             transparent;
--alv-list-item-bg-hover:       var(--alv-color-background-hover);
--alv-list-item-bg-active:      var(--alv-color-background-active);
--alv-list-item-text:           var(--alv-color-text-primary);
--alv-list-item-text-secondary: var(--alv-color-text-secondary);

Radio

--alv-radio-border:         var(--alv-color-border);
--alv-radio-border-hover:   var(--alv-color-border-hover);
--alv-radio-border-focus:   var(--alv-color-border-focus);
--alv-radio-bg:             var(--alv-color-background);
--alv-radio-checked-bg:     var(--alv-color-primary);
--alv-radio-checked-border: var(--alv-color-primary);
--alv-radio-text:           var(--alv-color-text-primary);
--alv-radio-text-disabled:  var(--alv-color-text-disabled);

Usage examples

Dark theme

Override the semantic tokens that matter for light/dark contrast. Place this in your global stylesheet — the SDK tokens are already scoped to :root, so your overrides just need to be present on the page.

[data-theme="dark"],
[data-theme="dark"] :root {
  --alv-color-primary:        #60a5fa;
  --alv-color-background:     #1f2937;
  --alv-color-surface:        #374151;
  --alv-color-text-primary:   #f9fafb;
  --alv-color-text-secondary: #d1d5db;
  --alv-color-border:         #4b5563;
  --alv-color-border-focus:   #60a5fa;
  --alv-input-bg:             #1f2937;
}

Or use the built-in preset:

<body data-theme="dark">...</body>

Compact layout

:root {
  --alv-spacing-md:     0.75rem;
  --alv-spacing-lg:     1rem;
  --alv-font-size-base: 1rem;
  --alv-font-size-lg:   1.25rem;
  --alv-input-height:   2.5rem;
  --alv-border-radius:  0.25rem;
}

Corporate branding

:root {
  --alv-color-primary:       #0066cc;
  --alv-color-primary-hover: #0052a3;
  --alv-font-family-sans:    "Inter", sans-serif;
  --alv-border-radius:       0.25rem;
  --alv-border-radius-lg:    0.375rem;
}
Element Components Link to a page in the guide Input Components Link to a page in the guide