Components
Layout
The application skeleton: Root, Header, Sider, Content, Footer — the admin arrangement dressed in the paper-and-ink surfaces. The root is a full-height grid; a declared `sider` turns the columns around to meet whichever edge the rail stands on.
#Basic usage
The content area takes the flow's full measure and holds it to a readable column.
Below the flow the footer rests at the foot of the grid, however little content there is.
<script setup lang="ts">
import { Button, Layout } from "@bysages/vue";
const stops = ["Overview", "Ledger", "Archive", "Settings"];
</script>
<template>
<!-- The border and paper make the skeleton read as a window on the
demo canvas — a bare grid melts into the page around it. -->
<Layout.Root
sider="start"
class="min-h-72 overflow-clip rounded-lg border border-border bg-surface-0"
>
<Layout.Sider>
<nav class="grid gap-(--bs-gap-xs) p-(--bs-padding-sm)">
<Button v-for="stop in stops" :key="stop" variant="ghost" class="justify-start!">
{{ stop }}
</Button>
</nav>
</Layout.Sider>
<Layout.Header>
<strong class="text-lg">The workbench</strong>
</Layout.Header>
<Layout.Content>
<div class="grid gap-(--bs-gap-md) max-w-[72ch]">
<p class="m-0">
The content area takes the flow's full measure and holds it to a readable column.
</p>
<p class="m-0">
Below the flow the footer rests at the foot of the grid, however little content there is.
</p>
</div>
</Layout.Content>
<Layout.Footer>
<p class="m-0 text-tertiary">By Sages Elements — the paper-and-ink system.</p>
</Layout.Footer>
</Layout.Root>
</template>
The rail folds on the caller's word and resizes by hand — drag the hairline at its edge, or hold an arrow key.
<script setup lang="ts">
import { Button, Layout } from "@bysages/vue";
import { reactive } from "vue";
const state = reactive({ collapsed: false, width: "16rem" });
const stops = [
{ icon: "i-lucide-layout-dashboard", label: "Overview" },
{ icon: "i-lucide-book-open", label: "Ledger" },
{ icon: "i-lucide-archive", label: "Archive" },
{ icon: "i-lucide-settings", label: "Settings" },
];
</script>
<template>
<div class="grid gap-4">
<Button variant="ghost" class="justify-self-start" @click="state.collapsed = !state.collapsed">
{{ state.collapsed ? "Expand the rail" : "Fold the rail" }}
</Button>
<Layout.Root
sider="start"
class="min-h-72 overflow-clip rounded-lg border border-border bg-surface-0"
>
<Layout.Sider
v-model:collapsed="state.collapsed"
v-model:width="state.width"
resizable
class="min-h-full"
>
<nav
class="grid gap-(--bs-gap-xs) p-(--bs-padding-sm)"
:style="state.collapsed ? { paddingInline: 'var(--bs-space-3)' } : undefined"
>
<Button
v-for="stop in stops"
:key="stop.label"
variant="ghost"
:class="state.collapsed ? 'justify-center!' : 'justify-start!'"
:aria-label="state.collapsed ? stop.label : undefined"
:title="state.collapsed ? stop.label : undefined"
:style="{
inlineSize: state.collapsed ? '2rem' : '100%',
blockSize: state.collapsed ? '2rem' : undefined,
paddingInline: state.collapsed ? '0' : 'var(--bs-padding-sm)',
overflow: 'hidden',
whiteSpace: 'nowrap',
}"
>
<Icon :name="stop.icon" />
<span v-if="!state.collapsed">{{ stop.label }}</span>
</Button>
</nav>
</Layout.Sider>
<Layout.Header>
<strong class="text-lg">The workbench</strong>
</Layout.Header>
<Layout.Content>
<div class="grid gap-(--bs-gap-md) max-w-[72ch]">
<p class="m-0">
The rail folds on the caller's word and resizes by hand — drag the hairline at its edge,
or hold an arrow key.
</p>
</div>
</Layout.Content>
<Layout.Footer>
<p class="m-0 text-tertiary">By Sages Elements — the paper-and-ink system.</p>
</Layout.Footer>
</Layout.Root>
</div>
</template>
#Props
#LayoutRoot
| Prop | Type | Default | Description |
|---|---|---|---|
sider | String as PropType<"start" | "end"> | undefined | — |
#LayoutSider
| Prop | Type | Default | Description |
|---|---|---|---|
collapsed | Boolean | false | — |
width | String | "16rem" | — |
collapsedWidth | String | "3.5rem" | — |
resizable | Boolean | false | — |
minWidth | String | "12rem" | — |
maxWidth | String | "24rem" | — |