Shape Set
A shape set is the proposed way to expose a shape library. For every interface, you publish the available shapes and the themes they support — giving consumers a complete map of what's available.
Below is the shape set for intershapes.com, built with Astro. Switch themes to inspect every shape across all four token sets.
Primitives
Atomic building blocks used across shapes. Styled with @scope([data-primitive]). No formal interface file — props defined inline.
Layout
<PageSection size="standard">…</PageSection><PageContainer width="7xl">…</PageContainer><Grid cols={3} gap="sm">…</Grid><Cluster gap="md" wrap={true}>…</Cluster>Content
<Avatar initials="AW" size="md" /><Badge text="Admin" /><Timestamp text="2 hours ago" size="sm" /><ColorDot color="interface" /> <!-- 'interface' | 'shape' | 'white' | 'green' | 'ink' --><NumberCircle number={1} color="interface" />Profile
3 shapes for the UserProfile interface.
export interface UserProfile {
name: string
initials: string
role: string
joinedAt?: string
}<ProfileHorizontal data={user} />Alan White
Admin<ProfileVertical data={user} />Written by AW Alan White in the design channel.
Written by <ProfileInline data={user} /> in the channel.Communication
Chat messages, comments, and notifications.
ChatMessage
export interface ChatMessage {
role: 'user' | 'ai'
content: string
color?: 'interface' | 'shape'
}How should I render a UserProfile in a sidebar?
<ChatMessageUser data={{ content: "How should I render a UserProfile?" }} theme="light" />Considering the sidebar context, a horizontal profile with compact spacing works best.
<ChatMessageAI data={{
content: "Use ProfileHorizontal with dark theme.",
variant: "done" <!-- 'reasoning' | 'sub' | 'done' -->
}} theme="light" <!-- 'light' | 'dark' | 'brand' | 'elevated' -->
/>Use ProfileHorizontal with the dark theme for sidebar context.
<ChatMessageAI data={{ content: "Use ProfileHorizontal...", variant: "done" }} />Comment
export interface Comment {
body: string
timestamp: string
}This looks great, love the separation of concerns here.
<CommentThread data={{ body: "Looks great!", timestamp: "2 hours ago" }} theme="light">
<span slot="author">Sarah Kim</span>
</CommentThread>Agreed, the interface/shape split makes this much cleaner.
<CommentCompact data={{ body: "Agreed.", timestamp: "45 min ago" }} theme="light">
<span slot="author">Marcus Jones</span>
</CommentCompact>Notification
export interface Notification {
title: string
body: string
timestamp: string
type?: 'info' | 'success' | 'warning' | 'error'
}New shape available
ProfileVertical has been added to the shape library.
<NotificationCard data={{
title: "New shape available",
body: "ProfileVertical has been added.",
timestamp: "Just now",
type: "info" <!-- 'info' | 'success' | 'warning' | 'error' -->
}} />Theme deployed
The dark theme has been published.
<NotificationCard data={{ title: "Theme deployed", body: "...", timestamp: "5 min ago", type: "success" }} />Token mismatch
The brand theme is missing --code-bg.
<NotificationCard data={{ title: "Token mismatch", body: "...", timestamp: "1 hour ago", type: "warning" }} />Build failed
Shape compilation failed.
<NotificationCard data={{ title: "Build failed", body: "...", timestamp: "2 hours ago", type: "error" }} />Interface updated
UserProfile v2 is now available.
<NotificationRow data={{
title: "Interface updated",
body: "UserProfile v2 is now available.",
timestamp: "3 min ago",
type: "info"
}} />Validation error
Required field 'name' is missing.
<NotificationRow data={{ title: "Validation error", body: "...", timestamp: "10 min ago", type: "error" }} />Content
Cards, callouts, code panels, principle cards, interface cards, and stat boxes.
Card
export interface Card {
title: string
accent?: 'interface' | 'shape' | 'ink'
}Interface layer
The stable contract that shapes implement.
<CardDefault data={{ title: "Interface layer", accent: "interface" }}>
<p>Content goes here.</p>
</CardDefault>Shape layer
Visual expressions that come and go freely.
<CardDefault data={{ title: "Shape layer", accent: "shape" }}>
<p>Visual expressions that come and go freely.</p>
</CardDefault>Theme layer
Colour tokens injected by the nearest ancestor.
<CardDefault data={{ title: "Theme layer", accent: "ink" }}>
<p>Colour tokens injected by the nearest ancestor.</p>
</CardDefault>Callout
export interface Callout {
title?: string
body: string
variant?: 'default' | 'interface' | 'muted'
}Key concept
Interfaces are stable contracts. Shapes are replaceable.
<CalloutBox data={{
title: "Key concept",
body: "Interfaces are stable contracts.",
variant: "interface" <!-- 'default' | 'interface' | 'muted' -->
}} />Interface tip
Keep interfaces minimal.
<CalloutBox data={{ title: "Interface tip", body: "Keep interfaces minimal.", variant: "interface" }} />Themes own colour. Shapes own spacing.
<CalloutBox data={{ body: "Themes own colour...", variant: "muted" }} />CodePanel
export interface CodePanel {
label: string
dotColor?: 'interface' | 'shape' | 'white' | 'green'
code: string
}interface Button {
label: string
variant: "primary" | "secondary"
} <CodePanelBlock data={{
label: "Interface — Button",
dotColor: "interface", <!-- 'interface' | 'shape' | 'white' | 'green' -->
code: highlightedHtml
}} theme="dark" <!-- 'light' | 'dark' | 'brand' | 'elevated' -->
/>const CardShape = () => (
<article><Avatar /><Name /></article>
)
The UserProfile interface defines the data contract.
The <CodePanelInline code="UserProfile" /> interface defines the contract.Principle
export interface Principle {
number: number
title: string
description: string
accent?: 'interface' | 'shape'
}Interfaces are canon
The type contract is the stable foundation.
<PrincipleCard data={{
number: 1,
title: "Interfaces are canon",
description: "The type contract is the stable foundation.",
accent: "interface" <!-- 'interface' | 'shape' -->
}} />Shapes are replaceable
Visual representations come and go freely.
<PrincipleCard data={{ number: 2, title: "Shapes are replaceable", description: "...", accent: "shape" }} />InterfaceCard
export interface InterfaceCard {
name: string
category: 'atom' | 'molecule' | 'organism' | 'template'
description: string
code: string
}UserProfile
Core user identity data.
interface UserProfile {
name: string
role: string
} <InterfaceCardFull data={{
name: "UserProfile",
category: "atom", <!-- 'atom' | 'molecule' | 'organism' | 'template' -->
description: "Core user identity data.",
code: highlightedHtml
}} />Notification
Alert or status message.
interface Notification {
title: string
type: "info" | "error"
} <InterfaceCardFull data={{ name: "Notification", category: "molecule", description: "...", code: html }} />StatBox
export interface StatBox {
value: string
label: string
detail?: string
}Interfaces
Stable contracts
<StatBoxCentered data={{ value: "22", label: "Interfaces", detail: "Stable contracts" }} />Shapes
Visual expressions
<StatBoxCentered data={{ value: "30", label: "Shapes", detail: "Visual expressions" }} />Themes
<StatBoxCentered data={{ value: "4", label: "Themes" }} />Controls
Filters, options, and result previews.
FilterBar
export interface FilterBar {
filters: { label: string; value: string }[]
active: string
}<FilterBarPill data={{
filters: [{ label: 'All', value: 'all' }, { label: 'Atoms', value: 'atom' }],
active: "all"
}} />ContextOption
export interface ContextOption {
label: string
description: string
active?: boolean
}<ContextOptionCard data={{
label: "Dashboard sidebar",
description: "Compact card for team lists"
}} /><ContextOptionCard data={{ label: "Profile page", description: "Full vertical layout", active: true }} /><ContextOptionCard data={{ label: "Inline mention", description: "Minimal inline reference" }} />ResultPreview
export interface ResultPreview {
label: string
shape: string
theme: string
}Alan White
Admin<ResultPreviewPanel label="Horizontal + Light">
<ProfileHorizontal data={user} />
</ResultPreviewPanel>Alan White
Admin<ResultPreviewPanel data={{ label: "Vertical + Dark" }}>
<ProfileVertical data={user} />
</ResultPreviewPanel>Process
Step-by-step flow shapes.
Step
export interface Step {
number: number
title: string
body: string
color?: 'interface' | 'shape' | 'ink'
}Define the interface
The interface is a TypeScript type that describes the data contract.
interface Button {
label: string
variant: "primary" | "secondary"
} <StepBlock data={{ number: 1, title: "Define the interface", body: "...", color: "interface" }}>
<CodePanelBlock data={{ label: "Interface", dotColor: "interface", code: html }} />
</StepBlock>Define
Create the interface contract.
<StepFlow data={{ number: 1, title: "Define", body: "Create the interface.", color: "interface" }} />Build shapes
Create visual representations.
<StepFlow data={{ number: 2, title: "Build shapes", body: "...", color: "shape" }} />Surface
Choose the right shape for context.
<StepFlow data={{ number: 3, title: "Surface", body: "...", color: "ink" }} />Data
Comparison tables for structured data display.
ComparisonRow
export interface ComparisonRow {
aspect: string
values: Record<string, string>
}| Aspect | Traditional | Intershapes |
|---|---|---|
| Component count | 24 variants | 1 interface + N shapes |
| Theme support | Props or CSS-in-JS | data-theme tokens |
| AI-readable | No standard | TypeScript interface |
<ComparisonTableRow data={{
headers: ['Traditional', 'Intershapes'],
rows: [{ aspect: 'Theming', values: { Traditional: 'Baked in', Intershapes: 'Injected' } }],
highlightColumn: "Intershapes"
}} />Page Sections
Full-width shapes for navigation, heroes, headings, CTAs, and footers.
NavBar
export interface NavBar {
logo: string
logoHref: string
links: { label: string; href: string }[]
}<NavBarFixed data={{ logo: "Intershapes", logoHref: "/", links: [{ label: 'Themes', href: '/themes' }] }} />SectionHeading
export interface SectionHeading {
id: string
title: string
subtitle?: string
number?: number
accent?: 'interface' | 'shape' | 'ink'
}The Intershapes model
Separate what from how. Inject the look.
<SectionHeadingDefault data={{ title: "The model", subtitle: "Separate what from how." }} />Define the interface
The type contract is the stable foundation.
<SectionHeadingDefault data={{
id: "step-1",
title: "Define the interface",
subtitle: "The type contract is the stable foundation.",
number: 1,
accent: "interface"
}} />SubpageHero
export interface SubpageHero {
title: string
titleAccent?: string
subtitle: string
backHref?: string
}<SubpageHeroStandard data={{ title: "Shapes &", titleAccent: "Themes", subtitle: "...", backHref: "/" }} />hello world <SubpageHeroWithCode data={{ title: "Code", titleAccent: "Examples", subtitle: "...", backHref: "/" }}>
<CodePanelBlock data={{ ... }} />
</SubpageHeroWithCode>Hero
export interface Hero {
title: string
titleAccent?: string
subtitle: string
attribution?: string
actions?: { label: string; href: string; variant: 'primary' | 'secondary' }[]
}<HeroFull data={{ title: "inter", titleAccent: "shapes", subtitle: "What will you shape today?", actions: [...] }} />CTA
export interface CTA {
heading: string
body: string
actions: { label: string; href: string; variant: 'primary' | 'secondary'; external?: boolean }[]
}Start building with Intershapes
Separate what from how. Ship components that scale.
<CTABanner data={{
heading: "Start building",
body: "Separate what from how.",
actions: [{ label: 'Go', href: '#', variant: 'primary' }]
}} />WireframeViewer
export interface WireframeViewer {
layers: number
label: string
}The three layers visualised as an exploded wireframe.
<WireframeViewerSticky data={{ layers: 3, label: "Interface / Shape / Theme" }}>
<p>Content here</p>
</WireframeViewerSticky>SiteFooter
export interface SiteFooter {
columns: { heading: string; links: { label: string; href: string; external?: boolean }[] }[]
attribution: string
copyright: string
}<SiteFooterSitemap data={{
columns: [{ heading: 'Resources', links: [...] }],
attribution: "Built with Intershapes",
copyright: "2024 Intershapes"
}} />