SDAIA
SDAIA LogoSDAIA Logo
Getting StartedBrands
IntroductionInstallationFAQBrands
ColorsTypographySpacingRadiusGrid layoutsAll Tokens
All Components
AccordionActivityArea ChartAudio PlayerAvatarAvatarGroupCarousel ImageChartChipCode SnippetColumn ChartContext MenuDigital StampDividerDonut ChartDraggable ListDropdownEmpty StateFiltrationFooterImage ViewerLine ChartListMedia GalleryMenuMetricNav BarNav HeaderNumberPDF ViewerPie ChartPopoverProgress CircleQuoteRadial StepperScatterSecond Nav HeaderSlideout MenuStructured ListTableTable of ContentTree ViewVertical TabsVideo Player
BreadcrumbButtonButton MenuButton SplitContent SwitcherDate PickerFloating ButtonHorizontal TabsLinkModalPaginationRadio ButtonSwitchTag
AutocompleteCheckboxFile UploadNumber InputRange SliderRich Text EditorSelectSignatureSliderText AreaText Input
AlertsBadgeCardFeedbackFeedback OverviewHelp IconLoading IndicatorNotification BannerNotification PopupNotificationsProgress BarRatingRating OverviewSkeletonSkeleton/AvatarsTooltip
Sdaia Logo Animation
Sdaia Touch ButtonsSdaia Touch Switch

Brands

SDAIA UI ships with two brand themes: Public (the default) and Private. A brand is more than a coat of paint — it controls the visual rules every component follows, including typography, border radii, padding and spacing, icon sizing, and the brand color palette. You opt into a brand by applying a single CSS class to an ancestor element in your app.

At a glance

Same components, same behavior, same accessibility — different visual freedom.

Public — strict DGA brand

Locked typography, radii, spacing, icon sizes, and colors. Guaranteed visual consistency across every government service.

Private — flexible brand

Same defaults, but every brand-level token can be overridden. Tailor the look to your product without forking components.

Default brand

If you do nothing, your app renders with the Public brand. The Private brand is only applied when the brand-private class is present on an ancestor of the rendered tree.

Public vs Private

Both brands ship with the same defaults — including IBM Plex Sans Arabic as the typeface — and the same component behavior. They differ in which of those defaults a designer or developer is allowed to bend. Public follows the DGA design standard strictly; Private is a relaxed brand intended for products that need more visual flexibility.

Public — strict DGA brand

The Public brand enforces the DGA visual rules and these cannot be bypassed. IBM Plex Sans Arabic is the official typeface and must not be replaced. Components ship with fixed radii, fixed padding and spacing scales, fixed icon sizes, and a locked brand color palette. This guarantees that every product using the Public brand is visually consistent with every other government service. Use Public for any service that must conform to DGA's design standard.

Private — flexible brand

The Private brand relaxes the DGA constraints. The typeface, radii, padding and spacing, icon sizes, and the brand color palette can all be customized through tokens — IBM Plex Sans Arabic remains the default, but you can swap it for any font your product needs. This gives product teams room to express a distinct identity while still inheriting the same component behavior, accessibility, and RTL support. Use Private for products that are not bound by the DGA visual standard, or for internal tools and bespoke brand experiences.

Side-by-side: what changes between Public and Private

These comparisons illustrate how the same component tokens behave under each brand. Values shown for Private are examples — the brand exposes the tokens so each product can ship its own scale.

Brand colors

The brand color palette is the most visible difference. Public uses the official DGA palette; Private exposes the same tokens for override. The values below are illustrative — your Private theme can ship any palette.

TokenPublic · LockedPrivate · Customizable
Primary 600--ds-color-brand-primary-600
Public
#1B8354
Private
#6E56CF

The default action color used across buttons, links, and active states.

Primary 500--ds-color-brand-primary-500
Public
#25935F
Private
#7C66D9
Primary 300--ds-color-brand-primary-300
Public
#7BC9A2
Private
#B7A4F0
Primary 50--ds-color-brand-primary-50
Public
#F3FCF6
Private
#F5F2FF

Border radius

Public locks every component to the DGA radius scale. Private exposes the same tokens so a brand can ship a sharper or softer look.

TokenPublic · LockedPrivate · Customizable
Radius SM--ds-radius-sm
Public
4px
Private
8px
Radius MD--ds-radius-md
Public
8px
Private
14px
Radius LG--ds-radius-lg
Public
12px
Private
20px
Radius XL--ds-radius-xl
Public
16px
Private
28px

Padding & spacing

The spacing scale governs density. Public uses the fixed DGA rhythm; Private can tune the scale to suit a denser or roomier interface.

TokenPublic · LockedPrivate · Customizable
Spacing SM--ds-spacing-sm
Public
6px
Private
8px
Spacing MD--ds-spacing-md
Public
8px
Private
12px
Spacing LG--ds-spacing-lg
Public
12px
Private
16px
Spacing XL--ds-spacing-xl
Public
16px
Private
24px

Icon sizes

Public enforces the DGA icon scale. Private exposes the size tokens for products that prefer a larger or smaller iconography.

TokenPublic · LockedPrivate · Customizable
Icon SM--ds-size-icon-sm
Public
16px
Private
18px
Icon MD--ds-size-icon-md
Public
20px
Private
22px
Icon LG--ds-size-icon-lg
Public
24px
Private
28px

Typography

Both brands ship with the same default typeface. In Public, that typeface is locked. In Private, you can swap it for any font your product needs by overriding the font tokens.

TokenPublic · LockedPrivate · Customizable
Display font--ds-brand-font-display
PublicIBM Plex Sans Arabic (locked)
PrivateIBM Plex Sans Arabic — or any custom font
Text font--ds-brand-font-text
PublicIBM Plex Sans Arabic (locked)
PrivateIBM Plex Sans Arabic — or any custom font

Static activation

Use static activation when your entire application always renders in one brand. Apply the brand class once on the document root and every descendant inherits it. This is the simplest setup and the easiest to render correctly during server-side rendering.

Recommended — on the <html> element

Placing the class on <html> matches how dark mode and document direction are typically set. It also avoids a flash of unstyled brand during hydration because the class is present before any React code runs.

1<!DOCTYPE html>
2<html lang="en" class="brand-private">
3 <body>
4 <!-- All SDAIA UI components inside render with the Private brand -->
5 </body>
6</html>

Alternative — on the <body> element

If your stack does not let you control the <html> tag (some CMS templates, embeddable widgets, micro-frontends), the class also works on <body> or on any wrapping container. The brand resolves from the nearest ancestor that carries the class.

1<body class="brand-private">
2 <!-- Components below render with the Private brand -->
3</body>

In Next.js (App Router)

Set the class in your root layout so it ships in the initial HTML payload. This guarantees the correct brand renders on the server and prevents a visual swap when the React tree hydrates.

1// app/layout.tsx
2export default function RootLayout({ children }: { children: React.ReactNode }) {
3 return (
4 <html lang="en" className="brand-private">
5 <body>{children}</body>
6 </html>
7 );
8}

Dynamic activation (toggleable)

Use dynamic activation when users should be able to switch brands at runtime — for example a brand picker in settings, or a preview tool. SDAIA UI ships a BrandModeProvider that manages the class on the document and persists the choice in localStorage.

1. Wrap your app in the provider

Mount BrandModeProvider once, as high in the tree as possible. It applies the brand class to the document root and keeps it in sync with state.

1// app/providers.tsx
2'use client';
3
4import { BrandModeProvider } from 'sdaia-ui';
5
6export function Providers({ children }: { children: React.ReactNode }) {
7 return <BrandModeProvider>{children}</BrandModeProvider>;
8}

2. Read and update the brand from any component

Use the useBrandMode hook to read the current brand and switch it. The hook returns { brand, setBrand } — brand is either 'public' or 'private'.

1'use client';
2
3import { useBrandMode } from 'sdaia-ui';
4
5export function BrandSwitch() {
6 const { brand, setBrand } = useBrandMode();
7
8 return (
9 <button onClick={() => setBrand(brand === 'public' ? 'private' : 'public')}>
10 Switch to {brand === 'public' ? 'Private' : 'Public'}
11 </button>
12 );
13}

Combining static and dynamic

It is safe to use both at once. Set a static class on <html> for the initial render, then mount BrandModeProvider on top — the provider will take over and reconcile state on the first client render. This gives you a correct SSR render and a runtime toggle.

Scoping a brand to a subtree

Because the brand class is resolved from the nearest ancestor, you can apply Private to one section of a page while the rest stays on Public. This is useful for preview tools, embedded brand showcases, or mixed-tenant dashboards.

1<main>
2 {/* Public brand here (inherited from <html>) */}
3 <Hero />
4
5 <section className="brand-private">
6 {/* Private brand applies to everything in this section */}
7 <PrivatePreview />
8 </section>
9
10 <Footer />
11</main>

Portals and overlays

Components that render into a portal — Tooltip, Popover, Menu, Select, DatePicker — live outside the DOM tree of the trigger. SDAIA UI already walks the trigger's ancestors and re-applies the brand class on the portal root, so you do not need to do anything extra. Just make sure your trigger is inside the scoped subtree.

Avoiding a flash of unstyled brand

If you set the brand only from client-side code, users may briefly see the Public brand before your script runs and swaps it to Private. To avoid this, either render the class statically on the server (recommended), or use a small inline script in the document head that reads the persisted brand from localStorage and applies the class before the React tree mounts.

1<!-- Place in <head>, before any other script -->
2<script>
3 (function () {
4 try {
5 var b = localStorage.getItem('brand-mode');
6 if (b === 'private') document.documentElement.classList.add('brand-private');
7 } catch (e) {}
8 })();
9</script>

What changes between brands

The brand class swaps the design-rule tokens that govern shape, density, and color. Typography, component behavior, accessibility, and RTL support are identical across brands.

Locked in Public, customizable in Private:

  • Typography — Public is locked to IBM Plex Sans Arabic; Private exposes the font tokens so you can ship a custom typeface.
  • Border radius scale — Public uses the fixed DGA radii; Private exposes the radius tokens for override.
  • Padding and spacing scale — Public locks the DGA spacing rhythm; Private lets you adjust spacing tokens to suit a denser or roomier layout.
  • Icon sizes — Public enforces the DGA icon size scale; Private allows alternative icon sizing.
  • Brand color palette — Public uses the official DGA colors only; Private exposes the brand color tokens so you can ship a custom palette.

Same across both brands:

  • Default typeface — both brands start with IBM Plex Sans Arabic; only Private can change it.
  • Component behavior — interaction patterns, keyboard handling, and focus management are identical.
  • Accessibility — WCAG-aligned semantics, ARIA, and screen reader behavior do not change between brands.
  • RTL support — directional mirroring works the same way in both brands.

Overriding Private brand tokens

Because Private exposes the brand tokens, you can override them anywhere the brand-private class applies. Define your values inside a CSS rule scoped to .brand-private — the cascade will pick them up automatically.

1/* app/brand-private.css */
2.brand-private {
3 /* Brand colors */
4 --ds-color-brand-primary-600: #6E56CF;
5 --ds-color-brand-primary-500: #7C66D9;
6 --ds-color-brand-primary-300: #B7A4F0;
7 --ds-color-brand-primary-50: #F5F2FF;
8
9 /* Radii */
10 --ds-radius-md: 14px;
11 --ds-radius-lg: 20px;
12
13 /* Spacing */
14 --ds-spacing-md: 12px;
15 --ds-spacing-lg: 16px;
16
17 /* Typography (optional — defaults to IBM Plex Sans Arabic) */
18 --ds-brand-font-display: 'Inter', system-ui, sans-serif;
19 --ds-brand-font-text: 'Inter', system-ui, sans-serif;
20}

Public tokens are locked on purpose

Do not override brand tokens inside an unscoped selector or inside :root — that would also affect the Public brand, which must stay aligned with the DGA standard. Always scope overrides to .brand-private (or to a more specific descendant if you want to scope further).

Troubleshooting

Common pitfalls when brand styles do not appear as expected.

  • The class is on a sibling, not an ancestor — brand styles only inherit downward. Move the class up to a common parent of the components you want themed.
  • A nested ancestor also carries brand-private — the nearest ancestor wins. Remove the inner class if you want to reset to Public.
  • Brand fonts are not loading — confirm Inter and IBM Plex Sans Arabic are included by your app shell (e.g. next/font in Next.js).
  • Brand flashes on first paint — set the class statically on <html> at SSR time, or use the inline-script snippet above.
FAQ
Colors
On this page