@skirbi/semtic

3 min

@skirbi/semtic defines what things are.

It is the authoring layer. It takes clean, readable HTML and produces real semantic HTML output — without classes, without styling opinions, without framework lock-in.

Installation

bash
npm install @skirbi/semtic

Register everything:

js
import '@skirbi/semtic/register-semtic';

Or selectively:

js
import { SemticForm, SemticInput } from '@skirbi/semtic';
SemticForm.register();
SemticInput.register();

What it is not

Semtic does not define styling. It does not ship colors, spacing, or typography.

For theming, use @skirbi/pinta.

Structural components

semtic-article

Structured article with header and body.

html
<semtic-article title="My Post" subtitle="A subtitle">
  <p>Content goes here.</p>
</semtic-article>

Attributes: title, subtitle, content-id.

Headers only render when they have content — no empty <header> elements.

semtic-section

Section with optional header generation.

html
<semtic-section title="Related posts">
  ...
</semtic-section>

semtic-panel

Semantic container built on <section>. Supports labelledbyaria-labelledby.

semtic-page

Wraps content in <main>.

semtic-header

Generates a semantic header with <h1> and <h2>.

html
<semtic-header title="Welcome" subtitle="Sign in to continue"></semtic-header>

semtic-actions

Footer-style container for grouped actions.

semtic-article-meta

Auto-generates semantic metadata.

html
<semtic-article-meta date="2026-04-13" author="Wesley" readingtime="4"
tags="css,html"></semtic-article-meta>

Attributes: date, readingtime, author, tags.

Forms

All form controls share a unified contract: real HTML controls, label support, required indicator, error slot, attribute forwarding, light DOM only.

semtic-form

Wraps <form>. An optional legend attribute generates a <fieldset>.

semtic-fieldset

Semantic grouping for form controls with description and error handling.

semtic-input

html
<semtic-input
  label="Email"
  type="email"
  name="email"
  required
></semtic-input>

Attributes: label, type, required-label, id, name, placeholder, value.

semtic-textarea

Textarea equivalent of semtic-input.

semtic-checkbox

Checkbox with unified behavior.

semtic-radio

Radio button with unified behavior.

semtic-select

Select control with full feature parity with HTMLElementSugarSelect.

Static options:

html
<semtic-select
  label="Division"
  name="division_id"
  required
  options='[{"id":1,"name":"A"},{"id":2,"name":"B"}]'
  jpath-label="name"
  jpath-value="id"
></semtic-select>

Remote options with search:

html
<semtic-select
  label="User"
  name="user_id"
  searchable
  endpoint="/api/users"
  param="q"
  jpath="data"
  jpath-label-template="{name} ({email})"
  jpath-value-template="user:{id}"
></semtic-select>

Error slot:

html
<semtic-select label="Division" required>
  <span slot="error">Please pick a division</span>
</semtic-select>

semtic-tooltip

Composable tooltip. Supports long-form nested usage or short attribute-based usage.

semtic-nav

Wraps a real <nav>.

html
<semtic-nav>
  <a href="/">Home</a>
  <a href="/blog">Blog</a>
</semtic-nav>

semtic-nav-item

Renders as <a> when href is provided, <button> otherwise. Active state via active attribute sets aria-current="page". Nested items are indented.

html
<semtic-nav-item href="/posts" active>Posts</semtic-nav-item>
<semtic-nav-item href="/series">
  Series
  <semtic-nav-item href="/series/one">One</semtic-nav-item>
</semtic-nav-item>

semtic-nav-section

Groups nav items with an optional label. Adjacent sections are separated by a border.

html
<semtic-nav-section label="Main">
  <semtic-nav-item href="/dashboard">Dashboard</semtic-nav-item>
</semtic-nav-section>

semtic-sidenav

Sidebar navigation with optional app label. Hidden on mobile, always visible at >= 64rem. Toggle via toggle(), open(), close(), or the semtic-sidenav-open attribute.

html
<semtic-sidenav label="MyApp">
  <semtic-nav-section label="Main">
    <semtic-nav-item href="/dashboard">Dashboard</semtic-nav-item>
  </semtic-nav-section>
</semtic-sidenav>

semtic-breadcrumb

html
<semtic-breadcrumb semtic-separator=">">
  <a href="/">Home</a>
  <a href="/blog">Blog</a>
  <span>Article</span>
</semtic-breadcrumb>

Outputs <nav aria-label="Breadcrumb"> with <ol><li> structure. Separator injection is opt-in.

Layout primitives

Structural only. No styling is shipped.

semtic-grid

html
<semtic-grid semtic-columns="1" semtic-columns-md="2" semtic-columns-lg="3" semtic-gap="4">
  <semtic-panel>One</semtic-panel>
  <semtic-panel>Two</semtic-panel>
  <semtic-panel>Three</semtic-panel>
</semtic-grid>

Attributes: semtic-columns, semtic-columns-sm, semtic-columns-md, semtic-columns-lg, semtic-gap.

semtic-flex

html
<semtic-flex semtic-justify="end" semtic-gap="2">
  <button type="button">Cancel</button>
  <button type="submit">Save</button>
</semtic-flex>

Attributes: semtic-direction, semtic-justify, semtic-align, semtic-gap.

semtic-stack

html
<semtic-stack semtic-gap="4">
  <h2>Title</h2>
  <p>Text</p>
</semtic-stack>

Attribute: semtic-gap.

Additional components

semtic-terminal

Terminal-style code block with optional title and copy button. Style via semtic-terminal-style="macos|linux|windows|none". Default is linux.

semtic-aside

Callout block with left border accent.

html
<semtic-aside variant="tip">Use this for pro tips.</semtic-aside>

Variants: note, info, warning, tip.

semtic-table

Data table with static and remote modes.

Static:

html
<semtic-table
  caption="Users"
  columns='["Name","Email"]'
  data='[{"Name":"Alice","Email":"alice@example.com"}]'
  semtic-table-variant="default"
  searchable
></semtic-table>

Remote (JSON:API):

html
<semtic-table
  endpoint="/api/users"
  jpath="data"
  jpath-columns='[
    {"label":"Name","jpath":"name","sortable":true},
    {"label":"Email","jpath":"email"}
  ]'
  searchable
  semtic-table-variant="default"
></semtic-table>

Variants: default (borders, padding, hover rows), zebra (same + striped rows).

Example: Login page

html
<html semtic-theme="client1">
  <body>
    <semtic-grid semtic-columns="1">
      <semtic-panel semtic-theme-tone="primary" semtic-theme-variant="solid">
        <semtic-stack semtic-gap="4">
          <semtic-header
            title="Welcome back"
            subtitle="Sign in to continue"
          ></semtic-header>

          <semtic-form>
            <semtic-stack semtic-gap="3">
              <semtic-input label="Email" type="email" name="email" required></semtic-input>
              <semtic-input label="Password" type="password" name="password" required></semtic-input>
              <semtic-flex semtic-justify="end">
                <button type="submit">Login</button>
              </semtic-flex>
            </semtic-stack>
          </semtic-form>
        </semtic-stack>
      </semtic-panel>
    </semtic-grid>
  </body>
</html>

Theming

Semtic does not ship styling. Add @skirbi/pinta and apply semtic-theme to opt in.

html
<html semtic-theme="mytheme">
  <semtic-panel semtic-theme-tone="primary" semtic-theme-variant="solid">
    ...
  </semtic-panel>
</html>