Skip to contentAsheeUIAsheeUI

Configuration

The configuration file, what it may state, and how a value resolves through the cascade.

AsheeUI resolves every visual decision through one cascade, so a value can be set once globally, per component, or per instance.

The cascade

instance props            <Button radius="lg" />
        ↓
components.<name>         config.components.button.radius
        ↓
global defaults           config.defaultRadius
        ↓
built-in fallback         the value the component documents

The first tier that provides a value wins. A component only ever reads the resolved value, so overriding one tier never requires touching another.

The configuration object

Pass a partial configuration to the provider:

import { AsheeUIProvider } from "@asheeui/web";

export function Root({ children }) {
  return (
    <AsheeUIProvider
      config={{
        defaultTheme: "system",
        defaultRadius: "md",
        components: {
          button: { radius: "full" },
        },
      }}>
      {children}
    </AsheeUIProvider>
  );
}

An independent file works the same way. It is a plain module that exports the object, and nothing reads it from disk: the application imports it, which is why no bundler plugin is needed.

// asheeui.config.ts
import type { ExternalConfig } from "@asheeui/web";

const config: ExternalConfig = {
  defaultTheme: "light",
  defaultRadius: "xs",
};

export default config;

A configuration file is optional. The provider falls back to the library defaults, and an inline object works just as well.

Global keys

Key Type Meaning
color colour configuration Replaces the built-in light and dark palettes
defaultTheme "light", "dark", "system", or a theme you defined Theme applied on first paint
defaultRadius size scale Corner rounding for the components that leave rounding to the application
defaultVariant variant scale Visual treatment for the components that leave the treatment to the application
defaultColor colour scale Accent colour for every component that has one
components per-component object Component-level defaults, keyed by the component's registration name

defaultRadius and defaultVariant only reach a component that leaves the axis open. A component that registers its own value has answered before the global default is consulted, so the key is not ignored, it never gets that far. Alert, Badge, Avatar, Chip, Skeleton, Toast, Pagination, Tabs, Radio and Switch register their radius, as does the Form for its submit control, and Tabs and Radio register their treatment. Their pages say which axis each one pins, and components.<name> is how you restate one of them.

Component keys

components accepts one entry per component. The key is the component's registration name: its export name lowercased, so SidebarLayout is sidebarlayout, DataTable is datatable and EmptyState is emptystate, not sidebarLayout or dataTable. Two components keep the camelCase spelling (multiSelect, resizableScreen), which is exactly why the safe habit is to copy the key from the props table of the component's page rather than to guess it: an unknown key is a warning in development and does nothing at all in a JSON config.

Each entry takes the same axes the component exposes as props:

config={{
  components: {
    button: { radius: "lg", variant: "bordered" },
    badge: { size: "sm", color: "success" },
    alert: { type: "warning" },
  },
}}

The theme system

The provider applies the resolved theme as a class on the document root (theme-light, theme-dark, or theme-<name> for a theme you defined) and writes the theme's colours into CSS custom properties (--ashee-background, --ashee-primary, and so on). It does that before the first paint, so a page never flashes the wrong theme.

Export Purpose
useTheme Reads the current theme and changes it
themeController The same operations outside React
THEME_STORAGE_KEY The localStorage key the preference is stored under

A defaultTheme of "system" follows the operating system preference and keeps following it while it changes. A theme name is only applied when the configuration defines a CSS block for it.

Scrollbars

Scrollbar theming is configuration rather than a component. Set it under components.scrollbar:

config={{
  components: {
    scrollbar: { width: "12px", radius: "8px", thumbBorder: "2px" },
  },
}}
Key Meaning
width Thickness of the scrollbar
radius Corner rounding of the thumb
trackRadius Rounding of the track; falls back to radius
thumbBorder Breathing room between the thumb and the track, drawn as a transparent border
gutter The native scrollbar-gutter value

Validation

Configuration is validated once per provider mount, so a mistake is reported rather than silently replaced:

  • an invalid value throws while the provider renders, naming the key and the value;
  • an unknown key warns in development and stays silent in production, because a stray key is usually a typo rather than a reason to take an application down.

Layout and pattern keys

The layout and pattern components resolve their whole option set through the same cascade, so an application states its page structure once:

config={{
  components: {
    container: { size: "xl" },
    section: { spacing: "xl" },
    stack: { gap: "lg" },
    grid: { gap: "lg", align: "stretch" },
    page: { containerSize: "xl", sticky: true },
    navbar: { variant: "bordered", align: "center" },
    hero: { align: "center", spacing: "xl" },
    footer: { variant: "muted" },
  },
}}
Key Options
container size, gutter, centered
section spacing, background, contained, containerSize, divider
stack direction, gap, align, justify, wrap
grid columns, columnsMd, columnsLg, gap, align
page contained, containerSize, spacing, sticky, divider
sidebarlayout side, sidebarWidth, stickySidebar
authlayout panel, align, mediaPosition, contentSize
hero align, spacing, background, mediaPosition, containerSize, contained
cta align, spacing, background, panel, containerSize, contained
featuregrid columns, columnsMd, columnsLg, gap, spacing, background, align, containerSize, contained
testimonials the same options as featuregrid
pricingcard variant, size
navbar position, variant, align, contained, containerSize
footer variant, spacing, contained, containerSize
emptystate type, size, panel, role
pininput length, mode, size, masked, radius, isDisabled, isInvalid
clipboard timeout, label, copiedLabel, variant, color, size, radius
centered axis, minHeight, contained, containerSize
split stackAt, ratio, gap, align, divider, stickyEnd
resizableScreen orientation, defaultSize, minSize, maxSize, step, minContainerWidth, minContainerHeight, hideHandle
marketinglayout skipLink, skipLinkLabel, background
docslayout navigationLabel, tocLabel, navigationWidth, stickyNavigation, stickyToc, skipLink, skipLinkLabel
searchinput size, color, clearable, label, hideLabel
stepper orientation, size, showDescriptions
datatable searchable, searchLabel, paginated, pageSize, showRowCount, emptyTitle, emptyDescription
loadingstate label, size, panel, minHeight
errorstate size, panel, role, retryLabel, detailLabel
fileupload buttonLabel, hint, size

grid is the one entry where an unstated breakpoint is a deliberate value: a columnsLg you never set inherits columnsMd, which inherits columns, so a grid that never changes shape only states columns.

Next

  • Components for the shared prop axes and the substitution API.
  • Accessibility for the behaviour the framework guarantees.
  • Puck for the block configuration built on these keys.
  • Migration for the policy in force from 1.0.