Composables

Callout

A contextual feedback component for alerts, notifications, and inline messages. Supports multiple colors, visual styles, sizes, and orientations through the recipe system.

Overview

The Callout is a contextual feedback element used for alerts, notifications, status messages, and inline notices. The useCalloutRecipe() composable creates a fully configured recipe with color, variant, size, and orientation options — plus compound variants that handle the color-variant combinations automatically.

The Callout recipe integrates directly with the default design tokens preset and generates type-safe utility classes at build time with zero runtime CSS.

Why use the Callout recipe?

The Callout recipe helps you:

  • Ship faster with sensible defaults: Get 9 colors, 4 visual styles, 3 sizes, and 2 orientations out of the box with a single composable call.
  • Maintain consistency: Compound variants ensure every color-variant combination follows the same design rules, including dark mode overrides.
  • Customize without forking: Override base styles, default variants, or filter out options you don't need — all through the options API.
  • Stay type-safe: Full TypeScript support means your editor catches invalid color, variant, size, or orientation values at compile time.
  • Integrate with your tokens: Every value references the design tokens preset, so theme changes propagate automatically.
  • Support flexible layouts: Horizontal and vertical orientations adapt to your content structure, from compact inline messages to detailed multi-line notices.

Usage

Register the recipe

Add the Callout recipe to a local Styleframe instance. The global styleframe.config.ts provides design tokens and utilities, while the component-level file registers the recipe itself:

src/components/callout.styleframe.ts
import { styleframe } from 'virtual:styleframe';
import { useCalloutRecipe } from '@styleframe/theme';

const s = styleframe();

const callout = useCalloutRecipe(s);

export default s;

Build the component

Import the callout runtime function from the virtual module and pass variant props to compute class names:

src/components/Callout.tsx
import { callout } from "virtual:styleframe";

interface CalloutProps {
    color?: "primary" | "secondary" | "success" | "info" | "warning" | "danger" | "light" | "dark" | "neutral";
    variant?: "solid" | "outline" | "soft" | "subtle";
    size?: "sm" | "md" | "lg";
    orientation?: "horizontal" | "vertical";
    title?: string;
    description?: string;
    icon?: React.ReactNode;
    dismissible?: boolean;
    onDismiss?: () => void;
    children?: React.ReactNode;
}

export function Callout({
    color = "neutral",
    variant = "subtle",
    size = "md",
    orientation = "horizontal",
    title,
    description,
    icon,
    dismissible = false,
    onDismiss,
    children,
}: CalloutProps) {
    const classes = callout({ color, variant, size, orientation });

    return (
        <div className={classes} role="alert">
            {icon && <span>{icon}</span>}
            <div>
                {title && <strong>{title}</strong>}
                {description && <p>{description}</p>}
                {children}
            </div>
            {dismissible && (
                <button onClick={onDismiss} aria-label="Dismiss">
                    &times;
                </button>
            )}
        </div>
    );
}

See it in action

Colors

The Callout recipe includes 9 color variants: the 6 semantic colors (primary, secondary, success, info, warning, danger) plus 3 neutral-spectrum colors (light, dark, neutral). Each color is combined with every visual style variant through compound variants, so you get consistent, predictable styling across all combinations — including dark mode overrides.

The neutral color adapts automatically: it uses a light appearance in light mode and a dark appearance in dark mode, making it the safest default for general-purpose callouts.

Color Reference

ColorTokenUse Case
primary@color.primaryPrimary brand messages, key highlights
secondary@color.secondarySecondary information, supporting messages
success@color.successPositive feedback, completions, confirmations
info@color.infoInformational messages, tips, notices
warning@color.warningCaution messages, pending states, deprecation notices
danger@color.dangerError messages, destructive warnings, critical alerts
light@color.white / @color.gray-*Light-themed callouts, minimal visual weight
dark@color.gray-900Dark-themed callouts, high visual weight
neutralAdaptiveDefault. Light appearance in light mode, dark in dark mode
Pro tip: Use semantic color names that describe purpose, not appearance. This makes it easier to update your palette without touching component code.

Variants

Four visual style variants control how the callout is rendered. Each variant is combined with the selected color through compound variants, so you always get the correct background, text, and border colors for your chosen color.

Solid

Filled background with light text. The most prominent style, ideal for critical alerts that demand immediate attention.

Outline

Transparent background with colored border and text. Useful for secondary messages that provide context without dominating the visual hierarchy.

Soft

Light tinted background with colored text. A gentle but visible style that works well for informational messages and tips.

Subtle

Light tinted background with colored text and a matching border. Combines the softness of the soft variant with the definition of outline. This is the default variant for the Callout recipe.

Sizes

Three size variants from sm to lg control the font size, padding, and gap of the callout.

Size Reference

SizeFont SizePadding (V / H)Gap
sm@font-size.xs@0.5 / @0.75@0.5
md@font-size.sm@0.75 / @1@0.75
lg@font-size.md@1 / @1.25@1

Orientation

The orientation variant controls the layout direction of the callout content. Two options are available: horizontal (default) and vertical.

Horizontal

Content flows left to right in a row. Icon, text content, and dismiss button align horizontally. Best for compact, single-line messages.

Vertical

Content stacks top to bottom in a column. Useful for callouts with longer descriptions, multiple paragraphs, or action buttons below the message.

Orientation Reference

OrientationFlex DirectionUse Case
horizontalrowCompact inline messages, single-line alerts
verticalcolumnLonger descriptions, multi-line content, action buttons

Accessibility

Callouts communicate important information to users. Follow these guidelines to ensure they are accessible to everyone.

Use appropriate ARIA roles

Choose the right role based on the callout's urgency:

  • role="alert" — For urgent, time-sensitive messages (errors, critical warnings). Screen readers announce these immediately.
  • role="status" — For non-urgent updates (success confirmations, progress notifications). Announced at the next convenient pause.
  • No role — For static informational content (tips, notes). Use a plain <div> or <aside>.
<!-- Urgent error message -->
<div role="alert" class="...">Payment failed. Please try again.</div>

<!-- Non-urgent status update -->
<div role="status" class="...">Your changes have been saved.</div>

<!-- Informational aside -->
<aside class="...">Tip: You can customize your dashboard in settings.</aside>

Do not rely on color alone

Callouts should never communicate meaning through color alone. Always include a descriptive title or message text. Pairing an icon with the text further reinforces the callout's severity for users who cannot distinguish colors, following WCAG 1.4.1 (Use of Color).

<!-- Correct: icon + descriptive text + color -->
<div role="alert" class="...">
    <span>&#9888;</span>
    <strong>Warning:</strong> Your session will expire in 5 minutes.
</div>

<!-- Avoid: color alone with vague text -->
<div class="...">Note</div>

Dismissible callouts

When a callout is dismissible, the dismiss button must be keyboard-accessible. Use a <button> element with aria-label="Dismiss" so screen readers can announce its purpose. After dismissal, move focus to the next logical element in the page.

Contrast ratios

The solid variant places light text (@color.white) on a colored background. All default color tokens meet the WCAG AA minimum contrast ratio of 4.5:1 for normal text. If you override the default colors, verify contrast ratios with a tool like the WebAIM Contrast Checker.

Good practice: When multiple callouts appear on the same page, use the variant prop to create visual hierarchy rather than relying solely on color differences. For example, use solid for critical errors and soft or subtle for informational notices.

Customization

Overriding Defaults

The useCalloutRecipe() composable accepts an optional second argument to override any part of the recipe configuration. Overrides are deep-merged with the defaults, so you only need to specify the properties you want to change:

src/components/callout.styleframe.ts
import { styleframe } from 'virtual:styleframe';
import { useCalloutRecipe } from '@styleframe/theme';

const s = styleframe();

const callout = useCalloutRecipe(s, {
    base: {
        borderRadius: '@border-radius.lg',
    },
    defaultVariants: {
        color: 'info',
        variant: 'soft',
        size: 'md',
        orientation: 'horizontal',
    },
});

export default s;

Filtering Variants

If you only need a subset of the available variants, use the filter option to limit which values are generated. This reduces the output CSS and keeps your component API focused:

src/components/callout.styleframe.ts
import { styleframe } from 'virtual:styleframe';
import { useCalloutRecipe } from '@styleframe/theme';

const s = styleframe();

// Only generate info and danger colors, with soft and subtle styles
const callout = useCalloutRecipe(s, {
    filter: {
        color: ['info', 'danger'],
        variant: ['soft', 'subtle'],
    },
});

export default s;
Good to know: Filtering also removes compound variants and adjusts default variants that reference filtered-out values, so your recipe stays consistent.

API Reference

useCalloutRecipe(s, options?)

Creates a full callout recipe with all variants and compound variant styling.

Parameters:

ParameterTypeDescription
sStyleframeThe Styleframe instance
optionsDeepPartial<RecipeConfig>Optional overrides for the recipe configuration
options.baseVariantDeclarationsBlockCustom base styles for the callout
options.variantsVariantsCustom variant definitions for the recipe
options.defaultVariantsRecord<keyof Variants, string>Default variant values for the recipe
options.compoundVariantsCompoundVariant[]Custom compound variant definitions for the recipe
options.filterRecord<string, string[]>Limit which variant values are generated

Variants:

VariantOptionsDefault
colorprimary, secondary, success, info, warning, danger, light, dark, neutralneutral
variantsolid, outline, soft, subtlesubtle
sizesm, md, lgmd
orientationhorizontal, verticalhorizontal

Learn more about recipes →

Best Practices

  • Choose colors by meaning, not appearance: Use success for confirmations, danger for errors, and warning for caution messages — this keeps your UI consistent when tokens change.
  • Use neutral for generic messages: The neutral color adapts to light and dark mode automatically, making it the safest default for general-purpose callouts.
  • Prefer subtle or soft for informational callouts: Reserve solid for critical alerts that demand immediate attention. Too many solid callouts create visual fatigue.
  • Pair an icon with the message: Icons reinforce the callout's severity and improve scannability, especially for users who cannot distinguish colors.
  • Use vertical orientation for longer content: When callouts contain multi-line descriptions or action buttons, vertical layout prevents cramped horizontal spacing.
  • Filter what you don't need: If your application only uses a few colors, pass a filter option to reduce generated CSS.
  • Override defaults at the recipe level: Set your most common variant combination as defaultVariants so component consumers write less code.
  • Include descriptive text: Callouts should not rely on color alone to convey meaning. Always include a title or description that communicates the message.

FAQ