Skip to article frontmatterSkip to article content
Tutorial

Common UI

Abstract

Common UI is a collection of web components (prefixed with ct-) exposed for building patterns.

Keywords:commontoolsUI

The philosophy of Common UI is inspired by Swift UI, the ‘default’ configuration should ‘just work’ if you use the correct building blocks together.

Our ui package is a web component library implemented in lit that interoperates with the Common Tools runtime to produce a Swift UI-like abstraction, this means our components are divided into layers:

System Components

ct-theme

Applies a set of theme variables to the entire subtree. Not all components respect the theme yet, but many do. See packages/ui/src/v2/components/theme-context.ts

const localTheme = {
  accentColor: cell("#3b82f6"),
  fontFace: cell("system-ui, -apple-system, sans-serif"),
  borderRadius: cell("0.5rem"),
};

// later...

return {
  [NAME]: "Themed Charm",
  [UI]: (
    <ct-theme theme={localTheme}>
      {/* all components in subtree are themed */}
    </ct-theme>
  )
}

Can be nested and overriden further down the subtree.

ct-render

Used to render a Cell that has a [UI] property into the DOM. Usually not required inside a pattern, used in the app shell itself.

<ct-render $cell={myCharm} />

ct-keybind (beta)

Register keyboard shortcuts with a handler. These registrations are mediated by packages/shell/src/lib/keyboard-router.ts in the shell to prevent conflicts with system shortcuts.

    <ct-keybind
        code="KeyN"
        alt
        preventDefault
        onct-keybind={createChatRecipe({ ... })}
    />

Layout Components

Layout components do not provide any content themselves, they are used to arrange other components. We draw quite directly from the Swift UI Layout Fundamentals.

ct-screen

Designed to represent content that could fill the entire screen or a panel / content area. This will expand to fill the available space. It offers two optional slots: header and footer.

When to use: your <main> or <div> is not growing to full the available space. Typically appears once at the root of a pattern’s [UI] tree:

<ct-screen>
  <ct-heading slot="header">
    Hello
  </ct-heading>

  <div>...</div>
  <div>...</div>

  <div slot="footer">
    World
  </div>
</ct-screen>

Inspired by this Swift UI convention. A Screen is just a View but it represents the kind of view that MIGHT fill a screen on some device.

ct-toolbar

Stack several actions into a horizontal bar, typically at the top of <ct-screen>.

<ct-screen>
  <ct-toolbar slot="header">
      <ct-button>A</ct-button>
      <ct-button>B</ct-button>
  </ct-toolbar>
</ct-screen>

Stacks are all you need

... almost. Just the horizontal and vertical stacks if you control the spacing and alignment.

ct-vstack

Stack items vertically, this is a layer over the CSS flexbox API. You can permuate gap, align, justify and reverse attributes to control the behavior.

When to use: any time you need to stack items vertically.

<ct-vstack gap="1" align="start" justify="stretch">
    <div>A</div>
    <div>B</div>
    <div>C</div>
</ct-vstack>

ct-hstack

Stack items horizontally, this is a layer over the CSS flexbox API. You can permuate gap, align, justify and reverse attributes to control the behavior.

When to use: toolbars, column layouts, grouping icons and buttons and text together.

<ct-hstack gap="1" align="start" justify="stretch">
    <div>A</div>
    <div>B</div>
    <div>C</div>
</ct-hstack>

ct-zstack

Currently missing, would allow similar control for layering items on top of one another. Swift UI ZStack.

ct-vscroll

Wrap tall vertical content in a scrollable container with control over autoscroll and scrollbar appearance. Inspired by SwiftUI ScrollView.

<ct-vscroll height="400px">
  <ct-vstack gap="4">
    <p>Long content...</p>
  </ct-vstack>
</ct-vscroll>

In practice we often use a specific set of properties if dealing with a “chat view” that scrolls:

<ct-vscroll flex showScrollbar fadeEdges snapToBottom />

Here flex will force the vscroll to expand without a fixed height. snapToBottom will automatically scroll to the bottom when new content is added.

ct-autolayout

Will attempt to lay out the children provided as best it can. Provides two slots for left and right sidebars (that can be toggled open/shut). On a wide view, items stack horizontally, on a medium view thet stack vertically and on mobile it converts to a tabbed view.

<ct-screen>
  <!-- Header slot - fixed at top -->
  <div slot="header">
    <h2>Header Section</h2>
  </div>

  <!-- ct-autolayout creates responsive multi-panel layout with optional sidebars -->
  <!-- tabNames: Labels for main content panels (shown as tabs on mobile) -->
  <!-- Shows all panels side-by-side in a grid -->
  <ct-autolayout tabNames={["Main", "Second"]}>
    <!-- Left sidebar - use slot="left" -->
    <aside slot="left">
      <h3>Left Sidebar</h3>
      <p>Left content</p>
      <ct-button>Left Button</ct-button>
    </aside>

    <!-- Main content panels - no slot attribute needed -->
    <!-- Number of divs should match number of tabNames -->
    <div>
      <h1>Main Content Area</h1>
      <p>This is the main content with sidebars</p>
      <ct-button>Main Button</ct-button>
    </div>

    <div>
      <h1>Second Content Area</h1>
      <p>This is the second content with sidebars</p>
      <ct-button>Second Button</ct-button>
    </div>

    <!-- Right sidebar - use slot="right" -->
    <aside slot="right">
      <h3>Right Sidebar</h3>
      <p>Right content</p>
      <ct-button>Right Button</ct-button>
    </aside>
  </ct-autolayout>

  <!-- Footer slot - fixed at bottom -->
  <div slot="footer">
    <p>Footer Section</p>
  </div>
</ct-screen>

ct-grid (stale)

ct-grid has not been used in production and likely doesn’t work, but the intention is to wrap the CSS Grid API and blend in ideas from Swift UI Grid.

ct-spacer (missing)

Swift UI Spacer

Composed Layouts

You can mix-and-match the above components to achieve practically any (standard) layout.

<ct-screen>
    <ct-toolbar slot="top">
        <ct-button>hello</ct-button>
    </ct-toolbar>
    <ct-autolayout>
        <ct-vstack slot="left">
            <ct-hstack gap="sm">
                <icon>question</icon>
                <button>hello</button>
                <ct-spacer />
                <button>hello</button>
            </ct-hstack>

            <ct-hstack gap="sm">
                <icon>question</icon>
                <button>hello</button>
            </ct-hstack>
        </ct-vstack>

        <ct-screen>
            <ct-vstack>
                <ct-grid rows="3" cols="4" gap="2">
                    ...
                </ct-grid>
            </ct-vstack>
        </ct-screen>

        <ct-vscroll slot="right">
            <ul>
                <li>Imagine this was long</li>
            </ul>
        </ct-vscroll>
    </ct-autolayout>
</ct-screen>

Visual Components

Input Components

Interactive / Complex Components

Chat Components

Unused/Unproven Components