Feat: Frontend app shell

This commit is contained in:
MangoPig
2026-06-14 15:21:02 +01:00
parent 282e6b604d
commit 883e8a8bcc
16 changed files with 949 additions and 3 deletions

View File

@@ -0,0 +1,85 @@
.topBar {
--topbar-control-size: var(--control-size-md);
min-height: 4rem;
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
align-items: center;
gap: var(--space-3);
padding: var(--space-3) var(--space-4) var(--space-3);
background: var(--color-surface);
}
.identity {
min-width: 0;
display: grid;
gap: 0;
}
.eyebrow {
@include text-caption;
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.08em;
}
.title {
@include text-title;
display: flex;
align-items: center;
gap: var(--space-2);
strong {
font: inherit;
font-weight: var(--font-weight-title);
}
}
.context {
color: var(--color-text-muted);
}
.actions {
display: flex;
align-items: center;
gap: var(--space-1);
}
.actionButton,
.themeButton {
height: var(--topbar-control-size);
display: inline-flex;
align-items: center;
justify-content: center;
@include interactive-frame();
}
.actionButton {
width: var(--topbar-control-size);
}
.themeButton {
width: auto;
padding-inline: var(--space-2);
gap: var(--space-1);
color: var(--color-text);
}
.actionButton,
.themeButton {
@include interactive-frame-hover();
}
.themeLabel {
@include text-label;
}
@include respond-down(mobile) {
.topBar {
grid-template-columns: minmax(0, 1fr) auto;
padding: var(--space-2) var(--space-3) var(--space-3);
}
.actions {
display: none;
}
}

View File

@@ -0,0 +1,45 @@
// Path: Frontend/src/components/shell/TopBar/TopBar.tsx
import { For, type JSX } from "solid-js";
import type { Theme } from "../../../helpers/theme";
import { ChevronDown } from "../../../lib/icons";
import { topBarActions } from "../data/shell.data";
import styles from "./TopBar.module.scss";
type TopBarProps = {
theme: Theme;
onToggleTheme: VoidFunction;
};
export const TopBar = (props: TopBarProps): JSX.Element => {
return (
<header class={styles.topBar}>
<div class={styles.identity}>
<span class={styles.eyebrow}>Moku Work</span>
<div class={styles.title}>
<strong>Workspace Shell</strong>
<span class={styles.context}>Moku / Product</span>
<ChevronDown size={16} strokeWidth={2} />
</div>
</div>
<button class={styles.themeButton} type="button" onClick={props.onToggleTheme}>
<span class={styles.themeLabel}>{props.theme === "dark" ? "Dark" : "Light"}</span>
</button>
<div class={styles.actions}>
<For each={topBarActions}>
{(item): JSX.Element => {
const Icon = item.icon;
return (
<button class={styles.actionButton} type="button" aria-label={item.label} title={item.label}>
<Icon size={18} strokeWidth={2} />
</button>
);
}}
</For>
</div>
</header>
);
};