Customize UI
Theme sufwan-local-test UI components with CSS variables, next/font, labels, formatting, and component slots
Customize sufwan-local-test UI
sufwan-local-test UI is designed to be embedded in partner apps without forking components. The supported customization surface is:
- CSS variables on the sufwan-local-test root for brand, typography, radius, shadow, and z-index tokens
AggProviderconfig for theme mode, labels, formatting, feature flags, search wiring, and wallet actions- component
className/classNamesprops for layout composition
Import @agg-build/ui/styles.css once, then load your app CSS after it. sufwan-local-test defaults are low
specificity, so :root overrides can work, and .agg-root overrides win because they are closer
to the sufwan-local-test UI tree.
Minimal integration
import "@agg-build/ui/styles.css";
import "./agg-theme.css";
import { QueryClient, QueryClientProvider } from "@agg-build/hooks";
import { createAggClient } from "@agg-build/sdk";
import { AggProvider } from "@agg-build/ui";
import type { ReactNode } from "react";
const client = createAggClient({
baseUrl: "https://api.t.sufwankhalid.com",
appId: "your-app-id",
wsUrl: "wss://ws.t.sufwankhalid.com/ws",
});
const queryClient = new QueryClient();
export function Providers({ children }: { children: ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
<AggProvider
client={client}
config={{
general: {
locale: "en-US",
theme: "light",
rootClassName: "acme-agg-theme",
},
features: {
enableAnimations: true,
enableLiveUpdates: true,
enableGradients: false,
showFeesBreakdown: true,
},
}}
>
{children}
</AggProvider>
</QueryClientProvider>
);
}import { HomePage } from "@agg-build/ui/pages";
export default function Page() {
return <HomePage withHeader />;
}AggProvider renders an .agg-root wrapper around its children. If you pass
general.rootClassName, that class is added to the same root so you can scope partner-specific
branding without affecting other sufwan-local-test embeds on the page.
Import order
Load sufwan-local-test styles first and partner overrides second:
import "@agg-build/ui/styles.css";
import "./agg-theme.css";Then put variables in your CSS:
.agg-root.acme-agg-theme {
--agg-color-primary: #2563eb;
--agg-color-primary-hover: #1d4ed8;
--agg-color-secondary: #ffffff;
--agg-color-secondary-hover: #f1f5f9;
--agg-color-border: #cbd5e1;
--agg-color-foreground: #0f172a;
--agg-color-muted-foreground: #475569;
--agg-color-on-primary: #ffffff;
--agg-radius-md: 0.625rem;
--agg-radius-full: 0.75rem;
--agg-shadow-card: 0 8px 20px rgb(15 23 42 / 0.08);
}Custom fonts
Set the sufwan-local-test font token on .agg-root. This affects sufwan-local-test typography utilities, primitives, pages,
modals, and portaled surfaces.
Plain CSS
.agg-root {
--agg-font-family-sans: "Your Font", ui-sans-serif, system-ui, sans-serif;
}You can also set the token on :root; a value on .agg-root wins if both exist.
Next.js next/font
Expose the generated font variable on an ancestor, then map it into the sufwan-local-test token.
import { Inter } from "next/font/google";
import { Providers } from "./providers";
import "./globals.css";
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
});
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={inter.variable}>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}.agg-root {
--agg-font-family-sans: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
}Light and dark themes
general.theme controls the class and data attributes applied to .agg-root. If you ship custom
dark tokens, match sufwan-local-test's dark selector shape so your values win in dark mode:
.dark .agg-root.acme-agg-theme:not(.light),
.agg-dark .agg-root.acme-agg-theme:not(.light),
.agg-root.acme-agg-theme.dark,
.agg-root.acme-agg-theme[data-agg-theme="dark"] {
--agg-color-primary: #8fb4ff;
--agg-color-primary-hover: #b4ccff;
--agg-color-secondary: #0b1220;
--agg-color-secondary-hover: #172036;
--agg-color-border: #334155;
--agg-color-foreground: #e2e8f0;
--agg-color-muted-foreground: #94a3b8;
--agg-color-on-primary: #121214;
--agg-shadow-card: 0 10px 28px rgb(2 6 23 / 0.45);
}HomePage discovery URL persistence
features.enableEventDiscoveryUrlPersistence enables browser-history persistence for the primary
HomePage section's category, subcategory path, venue, and sort filters. Defaults are omitted from
the URL, unrelated query parameters are preserved, and the flag is disabled by default.
<AggProvider
client={client}
config={{ features: { enableEventDiscoveryUrlPersistence: true } }}
>
<HomePage />
</AggProvider>Gradients
Keep semantic colors and gradient definitions separate. features.enableGradients controls whether
branded surfaces resolve to flat primary colors or gradient tokens.
<AggProvider
client={client}
config={{
features: {
enableGradients: true,
},
}}
>
<HomePage />
</AggProvider>.agg-root.acme-agg-theme {
--agg-gradient-brand: linear-gradient(90deg, #2563eb, #4f46e5);
--agg-gradient-brand-hover: linear-gradient(90deg, #1d4ed8, #4338ca);
}
.dark .agg-root.acme-agg-theme:not(.light),
.agg-dark .agg-root.acme-agg-theme:not(.light),
.agg-root.acme-agg-theme.dark,
.agg-root.acme-agg-theme[data-agg-theme="dark"] {
--agg-gradient-brand: linear-gradient(90deg, #8fb4ff, #c084fc);
--agg-gradient-brand-hover: linear-gradient(90deg, #b4ccff, #d8b4fe);
}Public theme tokens
Set these on .agg-root or .agg-root.<your-rootClassName>.
| Token | Usage |
|---|---|
--agg-color-primary | Primary CTA color and emphasis states |
--agg-color-primary-hover | Primary action hover color |
--agg-color-secondary | Card, popover, modal, and control surfaces |
--agg-color-secondary-hover | Neutral hover and selected surfaces |
--agg-color-border | Borders and separators |
--agg-color-foreground | Primary text and icon foreground |
--agg-color-muted-foreground | Secondary text, metadata, and helper labels |
--agg-color-on-primary | Text and icons on branded filled surfaces |
--agg-color-success | Positive status and bid-side color |
--agg-color-warning | Warning status |
--agg-color-error | Error status and ask-side color |
--agg-color-overlay | Modal and drawer overlay color |
--agg-gradient-brand | Branded gradient fill |
--agg-gradient-brand-hover | Branded gradient hover fill |
--agg-font-family-sans | Base sufwan-local-test UI font family |
--agg-font-size-2xs through --agg-font-size-4xl | Typography size scale |
--agg-line-height-3, --agg-line-height-14, --agg-line-height-4 through --agg-line-height-10, --agg-line-height-base | Typography line-height scale |
--agg-font-weight-normal, --agg-font-weight-medium, --agg-font-weight-semibold, --agg-font-weight-bold | Typography weight scale |
--agg-radius-sm, --agg-radius-md, --agg-radius-lg, --agg-radius-xl, --agg-radius-2xl, --agg-radius-full | Radius scale |
--agg-shadow-card, --agg-shadow-popover, --agg-shadow-modal | Elevation tokens |
--agg-z-index-dropdown, --agg-z-index-overlay, --agg-z-index-modal, --agg-z-index-toast, --agg-z-index-tooltip | Dropdown, overlay, modal, toast, and tooltip layering |
Avoid overriding bridge tokens such as --color-agg-*, --text-agg-*, and --font-agg-sans
directly. They are generated from the public --agg-* tokens.
Copy and labels
Use general.labels when you need to change user-facing text. Label overrides are deep-merged with
sufwan-local-test defaults, so you only provide the keys you want to replace.
<AggProvider
client={client}
config={{
general: {
labels: {
common: {
retry: "Try again",
},
auth: {
signIn: "Sign in to Acme",
deposit: "Add funds",
},
eventList: {
emptyTitle: "No markets match your filters",
},
},
},
}}
>
<HomePage />
</AggProvider>Use labels for product copy. Use CSS variables for visual styling. Use component props for data, callbacks, and layout.
Formatting
Override formatting functions when your app needs a different locale, currency presentation, or date style.
const usdCompact = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
notation: "compact",
maximumFractionDigits: 1,
});
<AggProvider
client={client}
config={{
general: {
locale: "en-US",
},
formatting: {
formatCompactCurrency: (value) => usdCompact.format(value),
},
}}
>
<HomePage />
</AggProvider>;Odds display
formatPercent and formatPrice are two renderings of the same number — both receive a
probability between 0 and 1. Discovery surfaces (event tiles, search results, charts) use
formatPercent; price surfaces (order book, event page outcome chips, place-order, positions)
use formatPrice.
To show a single odds convention app-wide, set both to the same function:
// Decimal odds: 0.62 -> "1.61"
const toDecimalOdds = (probability: number) => (1 / probability).toFixed(2);
<AggProvider
client={client}
config={{
formatting: {
formatPercent: toDecimalOdds,
formatPrice: toDecimalOdds,
},
}}
>
<HomePage />
</AggProvider>;formatPrice is optional. Leave it unset to keep the default cents rendering (62.5¢).
Two values deliberately stay on the built-in formatters even when formatPrice is set, because
neither is a price: the order book spread (a difference between two prices, always in cents)
and position PnL percentages.
Layout and slots
Use className / classNames for layout composition, not deep CSS selectors.
import { HomePage } from "@agg-build/ui/pages";
export function MarketHome() {
return (
<HomePage
withHeader={false}
classNames={{
root: "min-h-screen",
header: "border-b border-slate-200 bg-white/90 backdrop-blur",
tabs: "mx-auto w-full max-w-6xl px-6",
sections: "mx-auto w-full max-w-6xl px-6 py-8 gap-8",
}}
/>
);
}Prefer token overrides for colors, fonts, radius, shadows, and other repeated visual decisions. Use slot class names for spacing, containment, sticky behavior, or one-off layout requirements.
Troubleshooting
Related tools
Storybook Customization
Interactive token guide and live component catalog.
Theme Studio
Import a URL, inspect extracted brand tokens, and export CSS.
Setup Guide
Provider setup for SDK, hooks, UI components, and auth.
Components Overview
Curated references for pages, trading surfaces, events, and auth UI.