Layout

Panel

A bordered box in three parts: a header bar with a title and controls, a body, and a footer bar for actions.

#layout#card#section#no-js

Demo

Preview

Members

3 of 5 seats used

Ada LovelaceOwner
Grace HopperAdmin
Linus TorvaldsMember

Danger zone

Delete this project
Transfer ownership
These cannot be undone.

Activity

Live

A tinted header, no footer.

Get it

It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.

$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/panel.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/panel.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/panel.qwe
let panel_demo_people = [["Ada Lovelace", "Owner"], ["Grace Hopper", "Admin"], ["Linus Torvalds", "Member"]]view PanelDemo() =    <div.demo-stack>        <Panel id="panel-demo-members" title="Members" subtitle="3 of 5 seats used"               header={[<SearchInput name="q" field="panel-demo-q" placeholder="Find a person" size="sm"/>, <Button size="sm" id="panel-demo-invite">Invite</Button>]}               footer={[<Button variant="ghost" size="sm">Cancel</Button>, <Button size="sm">Save</Button>]}>            {for [n, r] in panel_demo_people => <div style="display: flex; justify-content: space-between; padding: 6px 0"><span>{n}</span><span style="color: var(--ui-muted)">{r}</span></div>}        </Panel>        <Grid columns={2}>            <Panel id="panel-demo-danger" title="Danger zone" tone="danger" padding="none"                   footer={<span style="font-size: 13px; color: var(--ui-muted)">These cannot be undone.</span>} footer_align="start">                <div style="padding: 14px 16px; border-bottom: 1px solid var(--ui-border); display: flex; justify-content: space-between; align-items: center">                    <span>Delete this project</span><Button tone="danger" variant="outline" size="sm">Delete</Button>                </div>                <div style="padding: 14px 16px; display: flex; justify-content: space-between; align-items: center">                    <span>Transfer ownership</span><Button variant="outline" size="sm">Transfer</Button>                </div>            </Panel>            <Panel id="panel-demo-subtle" title="Activity" tone="subtle" header={<Badge>Live</Badge>}>                <p>A tinted header, no footer.</p>            </Panel>        </Grid>    </div>

Source

ui/panel.qwe
// Panel: a bordered box in three parts: a header bar with a title and room for// controls, a body, and a footer bar for actions. For a settings block, a table// with its toolbar, a form with its buttons.////   <Panel title="Members" header={<Button size="sm">Invite</Button>} footer={<Pagination .../>}>//       <DataTable .../>//   </Panel>//   <Panel title="Danger zone" tone="danger" padding="none"> ... </Panel>//// title: its heading (an <h3>); subtitle: a line under it. header: HTML at the end of// the header bar (buttons, a search, a badge). footer: HTML in the footer bar, at the// right unless footer_align is "start" or "between". children: the body.// padding: the body's, "none" (for a table or a list that fills it), "sm", "md"// (default) or "lg". tone: "neutral" (default), "accent", "danger" or "subtle" (a// tinted header). id: the panel's id; its heading then names it for screen readers.import ui/theme.qwe use ui_clslet panel_css = css`.ui-panel {  --ui-panel-pad: 20px; display: flex; flex-direction: column; min-width: 0; border: 1px solid var(--ui-border);  border-radius: var(--ui-radius); background: var(--ui-surface); color: var(--ui-fg); font: 15px/1.5 var(--ui-font);}.ui-panel-pad-none { --ui-panel-pad: 0; }.ui-panel-pad-sm { --ui-panel-pad: 12px; }.ui-panel-pad-lg { --ui-panel-pad: 28px; }.ui-panel-head {  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; min-height: 52px;  padding: 10px 16px; border-bottom: 1px solid var(--ui-border);}.ui-panel-title { margin: 0; font-size: 15px; font-weight: 600; line-height: 1.35; }.ui-panel-sub { margin: 1px 0 0; color: var(--ui-muted); font-size: 13px; }.ui-panel-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-left: auto; }.ui-panel-body { flex: 1; padding: var(--ui-panel-pad); }.ui-panel-body > :first-child { margin-top: 0; }.ui-panel-body > :last-child { margin-bottom: 0; }.ui-panel-foot {  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px;  padding: 10px 16px; border-top: 1px solid var(--ui-border); background: var(--ui-subtle);  border-radius: 0 0 var(--ui-radius) var(--ui-radius);}.ui-panel-foot-start { justify-content: flex-start; }.ui-panel-foot-between { justify-content: space-between; }.ui-panel-subtle .ui-panel-head { background: var(--ui-subtle); border-radius: var(--ui-radius) var(--ui-radius) 0 0; }.ui-panel-accent { border-color: color-mix(in srgb, var(--ui-accent) 45%, var(--ui-border)); }.ui-panel-accent .ui-panel-head { background: var(--ui-accent-soft); color: var(--ui-accent); border-radius: var(--ui-radius) var(--ui-radius) 0 0; }.ui-panel-accent .ui-panel-sub { color: inherit; opacity: .8; }.ui-panel-danger { border-color: color-mix(in srgb, var(--ui-danger) 45%, var(--ui-border)); }.ui-panel-danger .ui-panel-head { background: var(--ui-danger-soft); color: var(--ui-danger); border-radius: var(--ui-radius) var(--ui-radius) 0 0; }.ui-panel-danger .ui-panel-sub { color: inherit; opacity: .8; }`view Panel(title: str? = null, subtitle: str? = null, header = null, footer = null, footer_align: str = "end",           padding: str = "md", tone: str = "neutral", id: str? = null, class: str? = null, children) {    style global panel_css    let hid = id != null && title != null ? `${id}-title` : null    <section class={ui_cls(["ui-panel", padding != "md" && `ui-panel-pad-${padding}`, tone != "neutral" && `ui-panel-${tone}`, class])}             id={id} aria-labelledby={hid}>        {(title != null || subtitle != null || header != null) && <header.ui-panel-head>            {(title != null || subtitle != null) && <div>                {title != null && <h3.ui-panel-title id={hid}>{title}</h3>}                {subtitle != null && <p.ui-panel-sub>{subtitle}</p>}            </div>}            {header != null && <div.ui-panel-tools>{header}</div>}        </header>}        <div.ui-panel-body>{children}</div>        {footer != null && <footer class={ui_cls(["ui-panel-foot", footer_align != "end" && `ui-panel-foot-${footer_align}`])}>{footer}</footer>}    </section>}
Thread
57Useful? Like it, or add to its thread below.

Thread 0 posts

On Panel: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.