Skip to content

VISOMI / FRONTEND UI

Components, built on native HTML.

Shared Tailwind recipes, native Angular and Astro controls. One visual language, with the browser doing what it does best.

37 component families

Examples use synthetic content. No data is sent or saved.

Actions

02

Buttons

button.classes()
View usage

Angular

<button appUI variant="secondary" size="sm">Cancel</button>
<button appUI size="icon" aria-label="Edit">…</button>

Astro

<Button variant="secondary" size="sm">Cancel</Button>
<Button size="icon" aria-label="Edit">…</Button>

Shared recipe

import { button } from '@visomi-stack/web-frontend-ui';

button.classes();

Typography

05

Headings

heading.classes()

A clear hierarchy

A clear hierarchy

A clear hierarchy

A clear hierarchy

View usage

Angular

<h2 appUI uiSize="lg">A clear hierarchy</h2>

Astro

<Heading as="h2" uiSize="lg">A clear hierarchy</Heading>

Shared recipe

import { heading } from '@visomi-stack/web-frontend-ui';

heading.classes();

Text

text.classes()

Good defaults leave room for your content.

Good defaults leave room for your content.

Supporting text stays readable in both themes.

Good defaults leave room for your content.

View usage

Angular

<p appUI uiSize="md">Good defaults.</p>
<p appUI tone="muted" uiSize="sm">Supporting text.</p>

Astro

<Text uiSize="md">Good defaults.</Text>
<Text tone="muted" uiSize="sm">Supporting text.</Text>

Shared recipe

import { text } from '@visomi-stack/web-frontend-ui';

text.classes();

Code

code.classes()
button.classes()
const ready = true;
button.classes({ variant: 'primary', size: 'md' });
View usage

Angular

<code appUI>button.classes()</code>
<pre><code appUI block>const ready = true;</code></pre>

Astro

<Code>button.classes()</Code>
<pre><Code block>const ready = true;</Code></pre>

Shared recipe

import { code } from '@visomi-stack/web-frontend-ui';

code.classes();

Dividers

divider.classes()


View usage

Angular

<hr appUI />
<hr appUI soft />

Astro

<Divider />
<Divider soft />

Shared recipe

import { divider } from '@visomi-stack/web-frontend-ui';

divider.classes();

Layout

03

Containers

container.classes()

Content stays centered within a standard Tailwind width.

View usage

Angular

<div appUI="container" width="sm">Content</div>

Astro

<Container width="sm">Content</Container>

Shared recipe

import { container } from '@visomi-stack/web-frontend-ui';

container.classes();

Cards

card.classes()

A place for focused work

Use surfaces and spacing to create a clear hierarchy.

Raised surface

Use surfaces and spacing to create a clear hierarchy.

Use surfaces and spacing to create a clear hierarchy.

Use surfaces and spacing to create a clear hierarchy.

Use surfaces and spacing to create a clear hierarchy.

Transparent surface

View usage

Angular

<article appUI tone="raised" padding="md">Content</article>

Astro

<Card tone="raised" padding="md">Content</Card>

Shared recipe

import { card } from '@visomi-stack/web-frontend-ui';

card.classes();

Application shell

05

Application shells

appShell.classes()

Desktop sidebar, mobile bottom navigation. Native links, shared styles, no layout runtime.

Open the full layout
View usage

Angular

<app-shell contentId="main" skipLabel="Skip to content">
  <app-sidebar label="Primary">...</app-sidebar>
  <header app-topbar>...</header>
  <h1>Workspace</h1>
  <nav appUI="navbar" aria-label="Primary">...</nav>
</app-shell>

Astro

<AppShell contentId="main" skipLabel="Skip to content">
  <Sidebar slot="sidebar" label="Primary">...</Sidebar>
  <Topbar slot="header">...</Topbar>
  <h1>Workspace</h1>
  <Navbar slot="mobile" aria-label="Primary">...</Navbar>
</AppShell>

Shared recipe

import { appShell } from '@visomi-stack/web-frontend-ui';

appShell.classes();

Application headers

topbar.classes()
View usage

Angular

<header app-topbar>
  <span data-slot="title">Workspace</span>
  <button appUI>Save</button>
</header>

Astro

<Topbar>
  <span slot="title">Workspace</span>
  <Button>Save</Button>
</Topbar>

Shared recipe

import { topbar } from '@visomi-stack/web-frontend-ui';

topbar.classes();

Data

05

Badges

badge.classes()
DraftIn progressReadyNeeds attentionUnavailable
View usage

Angular

<span appUI="badge" tone="success">Ready</span>

Astro

<Badge tone="success">Ready</Badge>

Shared recipe

import { badge } from '@visomi-stack/web-frontend-ui';

badge.classes();

Avatars

avatar.classes()
ML ML ML Workspace
View usage

Angular

<span appUI="avatar" uiSize="md" role="img" aria-label="Morgan Lee">ML</span>
<img appUI src="/favicon.svg" alt="Demo workspace" />

Astro

<Avatar label="Morgan Lee" uiSize="md">ML</Avatar>
<Avatar src="/favicon.svg" label="Demo workspace" />

Shared recipe

import { avatar } from '@visomi-stack/web-frontend-ui';

avatar.classes();

Tables

table.classes()

Rows become labelled cards below the md breakpoint. Column headers remain available to assistive technology.

Example workspaces
WorkspaceStatus
StudioReady
SandboxDraft
View usage

Angular

<table appUI mobileCards stickyHeaders role="table"><caption>Workspaces</caption>…</table>
<!-- Keep native header associations and explicit row/cell roles. Cell labels use <span data-slot="label" aria-hidden="true">…</span>. -->

Astro

<Table mobileCards stickyHeaders role="table"><caption>Workspaces</caption>…</Table>
<!-- Keep native header associations and explicit row/cell roles. Cell labels use <span data-slot="label" aria-hidden="true">…</span>. -->

Shared recipe

import { table } from '@visomi-stack/web-frontend-ui';

table.classes();

Description lists

descriptionList.classes()
Plan
Team
Members
8
Status
Ready
View usage

Angular

<dl appUI><dt>Plan</dt><dd>Team</dd></dl>

Astro

<DescriptionList><dt>Plan</dt><dd>Team</dd></DescriptionList>

Shared recipe

import { descriptionList } from '@visomi-stack/web-frontend-ui';

descriptionList.classes();

Pagination

pagination.classes()
View usage

Angular

<nav appUI="pagination" aria-label="Pages"><a appUI="button" href="?page=2" aria-current="page">2</a></nav>

Astro

<Pagination aria-label="Pages"><LinkButton href="?page=2" aria-current="page">2</LinkButton></Pagination>

Shared recipe

import { pagination } from '@visomi-stack/web-frontend-ui';

pagination.classes();

Forms

13

Fieldsets

fieldset.classes()
Example preferences
View usage

Angular

<fieldset appUI><legend appUI>Preferences</legend><label appUI inline><input appUI type="checkbox" /> Updates</label></fieldset>

Astro

<Fieldset><Label as="legend">Preferences</Label><Label inline><Checkbox /> Updates</Label></Fieldset>

Shared recipe

import { fieldset } from '@visomi-stack/web-frontend-ui';

fieldset.classes();

Fields

field.classes()
View usage

Angular

<app-field #nameField="appField" controlId="name" label="Name" description="Visible name."><input appUI [id]="nameField.controlId()" [attr.aria-describedby]="nameField.describedBy()" /></app-field>

Astro

<Field controlId="name" label="Name" description="Visible name."><Input id="name" aria-describedby="name-help" /></Field>

Shared recipe

import { field } from '@visomi-stack/web-frontend-ui';

field.classes();

Labels

label.classes()
View usage

Angular

<label appUI for="label-example">Name</label><input appUI id="label-example" />

Astro

<Label for="label-example">Name</Label><Input id="label-example" />

Shared recipe

import { label } from '@visomi-stack/web-frontend-ui';

label.classes();

Descriptions

description.classes()

Use a synthetic address for this example.

View usage

Angular

<input appUI id="email" aria-label="Email" aria-describedby="email-help" /><p appUI="description" id="email-help">Use an example address.</p>

Astro

<Input id="email" aria-label="Email" aria-describedby="email-help" /><Description id="email-help">Use an example address.</Description>

Shared recipe

import { description } from '@visomi-stack/web-frontend-ui';

description.classes();

Error messages

errorMessage.classes()
View usage

Angular

<input appUI id="email" aria-label="Email" aria-invalid="true" aria-describedby="email-error" /><p app-error id="email-error">Enter a valid address.</p>

Astro

<Input id="email" aria-label="Email" aria-invalid="true" aria-describedby="email-error" /><ErrorMessage id="email-error">Enter a valid address.</ErrorMessage>

Shared recipe

import { errorMessage } from '@visomi-stack/web-frontend-ui';

errorMessage.classes();

Text inputs

input.classes()

Errors expand after leaving an invalid field or checking the form. They collapse while editing and when valid.

View usage

Angular

<input appUI type="email" uiSize="sm" [size]="24" required />

Astro

<Input type="email" uiSize="sm" size={24} required />

Shared recipe

import { input } from '@visomi-stack/web-frontend-ui';

input.classes();

Textareas

textarea.classes()
View usage

Angular

<textarea appUI uiSize="md" id="message" rows="3" aria-label="Message"></textarea>

Astro

<Textarea uiSize="md" id="message" rows={3} aria-label="Message">Example text.</Textarea>

Shared recipe

import { textarea } from '@visomi-stack/web-frontend-ui';

textarea.classes();

Selects

select.classes()
View usage

Angular

<select appUI uiSize="md" aria-label="Region"><option value="am">Americas</option></select>

Astro

<Select uiSize="md" aria-label="Region"><option value="am" selected>Americas</option></Select>

Shared recipe

import { select } from '@visomi-stack/web-frontend-ui';

select.classes();

Checkboxes

checkbox.classes()
View usage

Angular

<label appUI inline><input appUI type="checkbox" name="updates" /> Receive updates</label>

Astro

<Label inline><Checkbox name="updates" /> Receive updates</Label>

Shared recipe

import { checkbox } from '@visomi-stack/web-frontend-ui';

checkbox.classes();

Radio buttons

radio.classes()
Choose a workspace type
View usage

Angular

<fieldset appUI><legend>Workspace type</legend><label appUI inline><input appUI type="radio" name="kind" value="personal" /> Personal</label></fieldset>

Astro

<Fieldset><legend>Workspace type</legend><Label inline><Radio name="kind" value="personal" /> Personal</Label></Fieldset>

Shared recipe

import { radio } from '@visomi-stack/web-frontend-ui';

radio.classes();

Switches

toggle.classes()
View usage

Angular

<label appUI inline><input appUI="switch" type="checkbox" /> Enable notifications</label>

Astro

<Label inline><Toggle /> Enable notifications</Label>

Shared recipe

import { toggle } from '@visomi-stack/web-frontend-ui';

toggle.classes();

Radio cards

radioCard.classes()
Choose a workspace type
View usage

Angular

<label appUI="radio-card"><input appUI type="radio" name="plan" value="standard" /><span>Standard</span></label>

Astro

<RadioCard><Radio name="plan" value="standard" /><span>Standard</span></RadioCard>

Shared recipe

import { radioCard } from '@visomi-stack/web-frontend-ui';

radioCard.classes();

Input groups

inputGroup.classes()
View usage

Angular

<div appUI="input-group"><span data-slot="prefix" aria-hidden="true">$</span><input appUI aria-label="Amount" /></div>

Astro

<InputGroup><span data-slot="prefix" aria-hidden="true">$</span><Input aria-label="Amount" /></InputGroup>

Shared recipe

import { inputGroup } from '@visomi-stack/web-frontend-ui';

inputGroup.classes();

Feedback and native overlays

04

Inline alerts

alert.classes()
Your example is ready

Status is expressed with a visible label, not color alone.

Needs attention

Status is expressed with a visible label, not color alone.

Unavailable

Status is expressed with a visible label, not color alone.

View usage

Angular

<div appUI="alert" tone="success" role="status"><strong>Ready</strong><p>Visible feedback.</p></div>

Astro

<Alert tone="success" role="status"><strong>Ready</strong><p>Visible feedback.</p></Alert>

Shared recipe

import { alert } from '@visomi-stack/web-frontend-ui';

alert.classes();

Loaders

loader.classes()
Loading exampleLoading example
View usage

Angular

<span role="status"><span appUI="loader" aria-hidden="true"></span><span class="sr-only">Loading</span></span>

Astro

<Loader label="Loading example" />

Shared recipe

import { loader } from '@visomi-stack/web-frontend-ui';

loader.classes();

Disclosures

disclosure.classes()
What is shared between applications?

The typed class recipes are shared. Each application keeps native markup, events and form behavior.

View usage

Angular

<details appUI><summary>What is shared?</summary><p>The class recipes.</p></details>

Astro

<Disclosure summary="What is shared?"><p>The class recipes.</p></Disclosure>

Shared recipe

import { disclosure } from '@visomi-stack/web-frontend-ui';

disclosure.classes();

Dialogs

dialog.classes()

Review the example

This native dialog contains focus while open and supports Escape. Closing returns focus to the trigger.

View usage

Angular

<button appUI (click)="example.showModal()">Open dialog</button>
<dialog appUI #example="appUIDialog" aria-labelledby="dialog-title"><h2 id="dialog-title">Review</h2><form method="dialog"><button appUI type="submit">Close</button></form></dialog>

Astro

<Button disabled data-ui-dialog-open="example-dialog">Open dialog</Button>
<Dialog id="example-dialog" aria-labelledby="dialog-title"><h2 id="dialog-title">Review</h2><form method="dialog"><Button type="submit">Close</Button></form></Dialog>
<!-- The docs enhancement script calls showModal(). -->

Shared recipe

import { dialog } from '@visomi-stack/web-frontend-ui';

dialog.classes();