General
Frame
Usage#
import { WDS_FRAME } from '@wds/angular/frame';Examples#
Default#
import { Component } from '@angular/core';
import { WdsFrameComponent, WdsFrameDescriptionComponent, WdsFrameFooterComponent, WdsFrameHeaderComponent, WdsFramePanelComponent, WdsFrameTitleComponent } from '@wds/angular/frame';
@Component({
selector: 'app-frame-default-demo',
imports: [WdsFrameComponent, WdsFrameHeaderComponent, WdsFrameTitleComponent, WdsFrameDescriptionComponent, WdsFramePanelComponent, WdsFrameFooterComponent],
template: `
<wds-frame class="w-full max-w-md">
<wds-frame-header>
<wds-frame-title>Frame title</wds-frame-title>
<wds-frame-description>Frame description</wds-frame-description>
</wds-frame-header>
<wds-frame-panel>Frame content</wds-frame-panel>
<wds-frame-footer>Frame footer</wds-frame-footer>
</wds-frame>
`,
})
export class FrameDefaultDemo {}Separated panels#
spacing.
Separated panel
Section description
Separated panel
Section description
import { Component } from '@angular/core';
import { WdsFrameComponent, WdsFrameDescriptionComponent, WdsFrameHeaderComponent, WdsFramePanelComponent, WdsFrameTitleComponent } from '@wds/angular/frame';
@Component({
selector: 'app-frame-separated-demo',
imports: [WdsFrameComponent, WdsFrameHeaderComponent, WdsFrameTitleComponent, WdsFrameDescriptionComponent, WdsFramePanelComponent],
template: `
<wds-frame class="w-full max-w-md">
<wds-frame-header>
<wds-frame-title>Section header</wds-frame-title>
<wds-frame-description>Description for the section</wds-frame-description>
</wds-frame-header>
<wds-frame-panel>
<h2 class="m-0 text-sm font-semibold">Separated panel</h2>
<p class="m-0 text-sm text-muted-foreground">Section description</p>
</wds-frame-panel>
<wds-frame-panel>
<h2 class="m-0 text-sm font-semibold">Separated panel</h2>
<p class="m-0 text-sm text-muted-foreground">Section description</p>
</wds-frame-panel>
</wds-frame>
`,
})
export class FrameSeparatedDemo {}Stacked panels#
stacked joins adjacent panels into one block: no space, no inner corners and a single border between them.
Stacked panel
Section description
Stacked panel
Section description
import { Component } from '@angular/core';
import { WdsFrameComponent, WdsFrameDescriptionComponent, WdsFrameHeaderComponent, WdsFramePanelComponent, WdsFrameTitleComponent } from '@wds/angular/frame';
@Component({
selector: 'app-frame-stacked-demo',
imports: [WdsFrameComponent, WdsFrameHeaderComponent, WdsFrameTitleComponent, WdsFrameDescriptionComponent, WdsFramePanelComponent],
template: `
<wds-frame stacked class="w-full max-w-md">
<wds-frame-header>
<wds-frame-title>Section header</wds-frame-title>
<wds-frame-description>Description for the section</wds-frame-description>
</wds-frame-header>
<wds-frame-panel>
<h2 class="m-0 text-sm font-semibold">Stacked panel</h2>
<p class="m-0 text-sm text-muted-foreground">Section description</p>
</wds-frame-panel>
<wds-frame-panel>
<h2 class="m-0 text-sm font-semibold">Stacked panel</h2>
<p class="m-0 text-sm text-muted-foreground">Section description</p>
</wds-frame-panel>
</wds-frame>
`,
})
export class FrameStackedDemo {}Dense#
dense removes the inset around the panels, so they sit flush with the frame border.
Dense panel
Section description
Dense panel
Section description
import { Component } from '@angular/core';
import { WdsFrameComponent, WdsFrameDescriptionComponent, WdsFrameHeaderComponent, WdsFramePanelComponent, WdsFrameTitleComponent } from '@wds/angular/frame';
@Component({
selector: 'app-frame-dense-demo',
imports: [WdsFrameComponent, WdsFrameHeaderComponent, WdsFrameTitleComponent, WdsFrameDescriptionComponent, WdsFramePanelComponent],
template: `
<wds-frame stacked dense class="w-full max-w-md">
<wds-frame-header>
<wds-frame-title>Section header</wds-frame-title>
<wds-frame-description>Description for the section</wds-frame-description>
</wds-frame-header>
<wds-frame-panel>
<h2 class="m-0 text-sm font-semibold">Dense panel</h2>
<p class="m-0 text-sm text-muted-foreground">Section description</p>
</wds-frame-panel>
<wds-frame-panel>
<h2 class="m-0 text-sm font-semibold">Dense panel</h2>
<p class="m-0 text-sm text-muted-foreground">Section description</p>
</wds-frame-panel>
</wds-frame>
`,
})
export class FrameDenseDemo {}Without outer border#
variant="ghost" drops the outer border.
The outer container of this frame has no border, only the background and panels are visible.
import { Component } from '@angular/core';
import { WdsFrameComponent, WdsFrameDescriptionComponent, WdsFrameHeaderComponent, WdsFramePanelComponent, WdsFrameTitleComponent } from '@wds/angular/frame';
@Component({
selector: 'app-frame-ghost-demo',
imports: [WdsFrameComponent, WdsFrameHeaderComponent, WdsFrameTitleComponent, WdsFrameDescriptionComponent, WdsFramePanelComponent],
template: `
<wds-frame variant="ghost" class="w-full max-w-md">
<wds-frame-header>
<wds-frame-title>No outer border</wds-frame-title>
<wds-frame-description>This frame uses variant="ghost" to remove the outer border.</wds-frame-description>
</wds-frame-header>
<wds-frame-panel>
<p class="m-0 text-sm text-muted-foreground">
The outer container of this frame has no border, only the background and panels are visible.
</p>
</wds-frame-panel>
</wds-frame>
`,
})
export class FrameGhostDemo {}Inverse#
variant="inverse" swaps the backgrounds: the frame takes the page background and the panels a muted one.
Billing
Next invoice on November 1
Usage
12,480 of 20,000 requests this month
import { Component } from '@angular/core';
import { WdsFrameComponent, WdsFrameDescriptionComponent, WdsFrameHeaderComponent, WdsFramePanelComponent, WdsFrameTitleComponent } from '@wds/angular/frame';
@Component({
selector: 'app-frame-inverse-demo',
imports: [WdsFrameComponent, WdsFrameHeaderComponent, WdsFrameTitleComponent, WdsFrameDescriptionComponent, WdsFramePanelComponent],
template: `
<wds-frame variant="inverse" class="w-full max-w-md">
<wds-frame-header>
<wds-frame-title>Inverse</wds-frame-title>
<wds-frame-description>The frame takes the page background, the panels a muted one.</wds-frame-description>
</wds-frame-header>
<wds-frame-panel>
<h2 class="m-0 text-sm font-semibold">Billing</h2>
<p class="m-0 text-sm text-muted-foreground">Next invoice on November 1</p>
</wds-frame-panel>
<wds-frame-panel>
<h2 class="m-0 text-sm font-semibold">Usage</h2>
<p class="m-0 text-sm text-muted-foreground">12,480 of 20,000 requests this month</p>
</wds-frame-panel>
</wds-frame>
`,
})
export class FrameInverseDemo {}Spacing#
spacing sets the padding of the panels, header and footer. The header and footer stay aligned with the panel content.
Panel content
Panel content
Panel content
Panel content
Panel content
Panel content
Panel content
Panel content
import { Component } from '@angular/core';
import { WdsFrameComponent, WdsFrameHeaderComponent, WdsFramePanelComponent, WdsFrameTitleComponent } from '@wds/angular/frame';
@Component({
selector: 'app-frame-spacing-demo',
imports: [WdsFrameComponent, WdsFrameHeaderComponent, WdsFrameTitleComponent, WdsFramePanelComponent],
template: `
<div class="grid w-full gap-4 sm:grid-cols-2">
<wds-frame spacing="xs">
<wds-frame-header>
<wds-frame-title>spacing="xs"</wds-frame-title>
</wds-frame-header>
<wds-frame-panel>
<p class="m-0 text-sm text-muted-foreground">Panel content</p>
</wds-frame-panel>
<wds-frame-panel>
<p class="m-0 text-sm text-muted-foreground">Panel content</p>
</wds-frame-panel>
</wds-frame>
<wds-frame spacing="sm">
<wds-frame-header>
<wds-frame-title>spacing="sm"</wds-frame-title>
</wds-frame-header>
<wds-frame-panel>
<p class="m-0 text-sm text-muted-foreground">Panel content</p>
</wds-frame-panel>
<wds-frame-panel>
<p class="m-0 text-sm text-muted-foreground">Panel content</p>
</wds-frame-panel>
</wds-frame>
<wds-frame spacing="default">
<wds-frame-header>
<wds-frame-title>spacing="default"</wds-frame-title>
</wds-frame-header>
<wds-frame-panel>
<p class="m-0 text-sm text-muted-foreground">Panel content</p>
</wds-frame-panel>
<wds-frame-panel>
<p class="m-0 text-sm text-muted-foreground">Panel content</p>
</wds-frame-panel>
</wds-frame>
<wds-frame spacing="lg">
<wds-frame-header>
<wds-frame-title>spacing="lg"</wds-frame-title>
</wds-frame-header>
<wds-frame-panel>
<p class="m-0 text-sm text-muted-foreground">Panel content</p>
</wds-frame-panel>
<wds-frame-panel>
<p class="m-0 text-sm text-muted-foreground">Panel content</p>
</wds-frame-panel>
</wds-frame>
</div>
`,
})
export class FrameSpacingDemo {}Custom radius#
--wds-frame-radius sets the outer radius. The panel radius follows it, minus the inset, so both corners stay parallel.
Auth service
Successfully logged in user: admin
Database
Query execution time: 12ms
Storage
Upload complete: image.png
import { Component } from '@angular/core';
import { WdsFrameComponent, WdsFrameDescriptionComponent, WdsFrameHeaderComponent, WdsFramePanelComponent, WdsFrameTitleComponent } from '@wds/angular/frame';
@Component({
selector: 'app-frame-radius-demo',
imports: [WdsFrameComponent, WdsFrameHeaderComponent, WdsFrameTitleComponent, WdsFrameDescriptionComponent, WdsFramePanelComponent],
template: `
<wds-frame stacked class="w-full max-w-sm" style="--wds-frame-radius: 6px">
<wds-frame-header>
<wds-frame-title>Server logs</wds-frame-title>
<wds-frame-description>Recent activity and errors</wds-frame-description>
</wds-frame-header>
<wds-frame-panel>
<h2 class="m-0 text-sm font-semibold">Auth service</h2>
<p class="m-0 text-sm text-muted-foreground">Successfully logged in user: admin</p>
</wds-frame-panel>
<wds-frame-panel>
<h2 class="m-0 text-sm font-semibold">Database</h2>
<p class="m-0 text-sm text-muted-foreground">Query execution time: 12ms</p>
</wds-frame-panel>
<wds-frame-panel>
<h2 class="m-0 text-sm font-semibold">Storage</h2>
<p class="m-0 text-sm text-muted-foreground">Upload complete: image.png</p>
</wds-frame-panel>
</wds-frame>
`,
})
export class FrameRadiusDemo {}With footer#
wds-frame-footer holds notes or actions below the panels.
Owner
1 of 5 seats used
import { Component } from '@angular/core';
import { WdsFrameComponent, WdsFrameDescriptionComponent, WdsFrameFooterComponent, WdsFrameHeaderComponent, WdsFramePanelComponent, WdsFrameTitleComponent } from '@wds/angular/frame';
import { WdsButtonComponent } from '@wds/angular/button';
@Component({
selector: 'app-frame-footer-demo',
imports: [WdsFrameComponent, WdsFrameHeaderComponent, WdsFrameTitleComponent, WdsFrameDescriptionComponent, WdsFramePanelComponent, WdsFrameFooterComponent, WdsButtonComponent],
template: `
<wds-frame class="w-full max-w-md">
<wds-frame-header>
<wds-frame-title>Team members</wds-frame-title>
<wds-frame-description>Invite people to collaborate on this project.</wds-frame-description>
</wds-frame-header>
<wds-frame-panel>
<h2 class="m-0 text-sm font-semibold">Owner</h2>
<p class="m-0 text-sm text-muted-foreground">olivia@example.com</p>
</wds-frame-panel>
<wds-frame-footer>
<div class="flex items-center justify-between gap-2">
<p class="m-0 text-sm text-muted-foreground">1 of 5 seats used</p>
<wds-button size="sm">Invite</wds-button>
</div>
</wds-frame-footer>
</wds-frame>
`,
})
export class FrameFooterDemo {}Fit#
fit sizes a panel to its content.
fit: sized to its content
Fills the remaining height
import { Component } from '@angular/core';
import { WdsFrameComponent, WdsFrameDescriptionComponent, WdsFrameHeaderComponent, WdsFramePanelComponent, WdsFrameTitleComponent } from '@wds/angular/frame';
@Component({
selector: 'app-frame-fit-demo',
imports: [WdsFrameComponent, WdsFrameHeaderComponent, WdsFrameTitleComponent, WdsFrameDescriptionComponent, WdsFramePanelComponent],
template: `
<wds-frame class="h-72 w-full max-w-md">
<wds-frame-header>
<wds-frame-title>Fit</wds-frame-title>
<wds-frame-description>Panels fill the height of the frame unless they have fit.</wds-frame-description>
</wds-frame-header>
<wds-frame-panel fit>
<p class="m-0 text-sm text-muted-foreground">fit: sized to its content</p>
</wds-frame-panel>
<wds-frame-panel>
<p class="m-0 text-sm text-muted-foreground">Fills the remaining height</p>
</wds-frame-panel>
</wds-frame>
`,
})
export class FrameFitDemo {}API References#
wds-frame#
stacked joins them into one block.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| variant | WdsFrameVariant | 'default' | inverse swaps the backgrounds of the frame and the panels, ghost drops the outer border. |
| spacing | WdsFrameSpacing | 'default' | |
| stacked | boolean | false | |
| dense | boolean | false |
Slots
| Slot | Description |
|---|---|
| (default) | wds-frame-header, wds-frame-panel and wds-frame-footer elements. |
CSS parts (::part)
| Part | Description |
|---|---|
| frame |
CSS custom properties
| Property | Description |
|---|---|
| --wds-frame-radius | --wds-radius-xl). |
| --wds-frame-panel-radius | |
| --wds-frame-gap | |
| --wds-frame-padding | |
| --wds-frame-background | |
| --wds-frame-border | |
| --wds-frame-panel-background | |
| --wds-frame-panel-border |
wds-frame-panel#
wds-frame. Its padding follows the frame's spacing.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| fit | boolean | false |
Slots
| Slot | Description |
|---|---|
| (default) |
CSS custom properties
| Property | Description |
|---|---|
| --wds-frame-panel-padding | spacing). |
wds-frame-header#
Slots
| Slot | Description |
|---|---|
| (default) | wds-frame-title, wds-frame-description or any content. |
wds-frame-title#
wds-frame-header.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| level | number | 3 | aria-level). |
Slots
| Slot | Description |
|---|---|
| (default) |
wds-frame-description#
wds-frame-title.
Slots
| Slot | Description |
|---|---|
| (default) |
wds-frame-footer#
Slots
| Slot | Description |
|---|---|
| (default) |
Accessibility#
Screen reader#
wds-frame is a presentational container: it adds no landmark or role. Wrap it in a <section> with aria-labelledby when it marks a section of the page.
wds-frame-title is a heading (role="heading") with aria-level from level (default 3). Set level so it fits the page outline.