
Let containers explain how their content should behave
A dialog, a card and the application shell have different responsibilities. A surface policy describes those responsibilities—such as focus handling, scrolling or content presentation—in a form participating children can observe.
Keep visual choices in the theme and shared behavior in the policy. A local exception can then change the behavior without making nearby components infer it from CSS.
Example — A dialog with one focus owner
A dialog keeps keyboard interaction inside its active content while it is open. When the host dialog already manages focus, the inner surface leaves that job to the host instead of installing a second trap.
For engineers
Wonder Todos assigns the main landmark to its canonical shell in design-system.config.ts:
surfacePolicies: {
[SurfaceVariant.SHELL]: {
a11y: {
landmarkRole: 'main',
},
},
},
This is a selected configuration block. The shell default does not claim main, allowing an application to choose its owner. A nested shell used only as layout context must avoid claiming a second main region.
Surface merges framework defaults, theme policy, application policy and its call-site policyOverride, in that order. The result is available through useSurfacePolicy; supported values are also emitted as CSS variables and attributes. Current Surface is exported from @wildo-ai/saas-frontend-lib.
Make inheritance an explicit choice
This illustrative composition gives the parent a single-line text policy and lets its child opt into that policy. useSurfacePolicy exposes the resolved value to participating child components; merely nesting arbitrary HTML does not apply truncation to it.
import { Surface } from '@wildo-ai/saas-frontend-lib';
import { SurfaceVariant } from '@wildo-ai/presets-components-models';
<Surface variant={SurfaceVariant.BASE}
policyOverride={{ content: { textTruncation: 'ellipsis-line' } }}>
<Surface variant={SurfaceVariant.BASE}
policyOverride={{ content: { textTruncation: 'inherit' } }}>
{content}
</Surface>
</Surface>
content represents application content using the framework’s policy-aware text primitives. With inherit, the child resolves the parent’s value. With reset, it resolves the framework default for that slot instead. Omitting a slot uses the normal variant/theme/application policy chain; nesting alone is not an instruction to copy every parent value.
Give a dialog one behavior owner
When adapting a dialog primitive that already owns focus trapping and scroll lock, use Surface on its content root with asChild and a11yManagedByHost. The existing host must supply a labelled dialog and keyboard/focus behavior; the flag delegates those responsibilities, it does not implement them.
<Surface variant={SurfaceVariant.BASE} asChild a11yManagedByHost
policyOverride={{ a11y: { landmarkRole: 'none' } }}>
<DialogContent aria-labelledby={titleId}>
<DialogTitle id={titleId}>{dialogTitle}</DialogTitle>
{content}
</DialogContent>
</Surface>
This is an adapter fragment inside an already configured dialog root. DialogContent and DialogTitle denote that host’s primitives; titleId, the localized title and content belong to the application. Use the host’s public API rather than duplicating its focus handlers. The framework’s standard dialog already composes its own Surface, so do not add a second wrapper around it merely to repeat this pattern.
Connect policy to the CSS consumers
The runtime’s stylesheet contract requires:
@import "tailwindcss";
@import "tw-animate-css";
@plugin "@wildo-ai/saas-frontend-lib/tailwind";
The plugin translates policy variables into layout and presentation rules. Animation utilities supply overlay transitions; duration tokens alone do not create their keyframes.
| Policy concern | Where the effect is applied |
|---|---|
| Focus and scroll containment | Surface’s shared handlers, unless a11yManagedByHost delegates them to the host |
| Landmark and accessible name | The container that owns the region, with header or explicit labelling |
| Content and automatic field presentation | Participating text, table and generated-field components |
| Portal target and mobile shape | Overlay hosts and portal adapters |
A policy is not global CSS that transforms arbitrary children. Use the framework’s participating primitives, and adapt third-party containers deliberately. For a local visual tweak use classes; for a change children must understand use policyOverride, without setting the same property through both paths.