Customize UI
Theme AGG UI components with CSS variables, next/font, labels, formatting, and component slots
Customize AGG UI
AGG UI is designed to be embedded in partner apps without forking components. The supported customization surface is:
- CSS variables on the AGG 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. AGG defaults are low
specificity, so :root overrides can work, and .agg-root overrides win because they are closer
to the AGG UI tree.
Minimal integration
import "@agg-build/ui/styles.css";
import "./agg-theme.css";
import { AggProvider, QueryClient, QueryClientProvider } from "@agg-build/hooks";
import { createAggClient } from "@agg-build/sdk";
import type { ReactNode } from "react";
const client = createAggClient({
baseUrl: "https://api.agg.market",
appId: "your-app-id",
wsUrl: "wss://ws.agg.market/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 AGG embeds on the page.
Import order
Load AGG 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 AGG font token on .agg-root. This affects AGG 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 AGG 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 AGG'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);
}
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 AGG 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
AGG 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
Make sure @agg-build/ui/styles.css is imported before your app CSS, and set
--agg-font-family-sans on .agg-root. With next/font, put the generated font variable on
an ancestor of AggProvider.
Scope variables to .agg-root and pass a stable general.rootClassName if you have multiple
embeds. AGG portaled surfaces also carry .agg-root, so the same scoped token block applies.
Add a dark-mode override block using AGG's selector shape. Light tokens and dark tokens are separate.
AGG internals use AGG token utilities. Override --agg-* variables for global theming, and use
documented className / classNames props for layout slots.