feat(FN-1018): isolate header brand spacing with .header-brand container
- Wrap logo + title in a .header-brand div for independent flex layout - Replace hard-coded gap/margin with gap utilities for consistent spacing - Add .header-brand CSS class with align-items: center and gap - Add unit test verifying brand container structure - Document .header-brand lockup pattern in README
This commit is contained in:
@@ -361,6 +361,8 @@ The Fusion logo and all task-creation action buttons (including the "+ New Task"
|
||||
|
||||
All color themes automatically provide values for these tokens. Adding a new color theme requires only setting `--todo` (and optionally `--logo-accent` if the logo should differ from the todo column color).
|
||||
|
||||
**Header brand lockup:** The logo SVG and "Fusion" wordmark are wrapped in a dedicated `.header-brand` flex container with `gap: var(--space-xs)` (4 px). This isolates logo-to-wordmark spacing from the wider `.header-left` gap (`var(--space-sm)`, 8 px) so the brand mark can be tuned independently of the spacing between the brand group and adjacent header controls (project selector, back button, etc.).
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
The dashboard includes several runtime safeguards to stay responsive during long sessions and on larger boards:
|
||||
|
||||
@@ -248,21 +248,23 @@ export function Header({
|
||||
return (
|
||||
<header className="header">
|
||||
<div className="header-left">
|
||||
<svg
|
||||
className="header-logo"
|
||||
width={24}
|
||||
height={24}
|
||||
viewBox="0 0 128 128"
|
||||
fill="none"
|
||||
aria-label="Fusion logo"
|
||||
role="img"
|
||||
>
|
||||
<circle cx="44" cy="44" r="20" fill="currentColor" />
|
||||
<circle cx="84" cy="44" r="20" fill="currentColor" />
|
||||
<circle cx="44" cy="84" r="20" fill="currentColor" />
|
||||
<circle cx="84" cy="84" r="20" fill="currentColor" />
|
||||
</svg>
|
||||
<h1 className="logo">Fusion</h1>
|
||||
<div className="header-brand">
|
||||
<svg
|
||||
className="header-logo"
|
||||
width={24}
|
||||
height={24}
|
||||
viewBox="0 0 128 128"
|
||||
fill="none"
|
||||
aria-label="Fusion logo"
|
||||
role="img"
|
||||
>
|
||||
<circle cx="44" cy="44" r="20" fill="currentColor" />
|
||||
<circle cx="84" cy="44" r="20" fill="currentColor" />
|
||||
<circle cx="44" cy="84" r="20" fill="currentColor" />
|
||||
<circle cx="84" cy="84" r="20" fill="currentColor" />
|
||||
</svg>
|
||||
<h1 className="logo">Fusion</h1>
|
||||
</div>
|
||||
|
||||
{/* Project Selector - shown when 2+ projects on desktop only */}
|
||||
{!isCompact && projects.length > 1 && (
|
||||
|
||||
@@ -62,6 +62,21 @@ describe("Header", () => {
|
||||
expect(logo.compareDocumentPosition(h1) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
||||
});
|
||||
|
||||
it("renders logo and wordmark inside a .header-brand container", () => {
|
||||
const { container } = render(<Header />);
|
||||
const brand = container.querySelector(".header-brand");
|
||||
expect(brand).not.toBeNull();
|
||||
// Brand container should contain the logo SVG
|
||||
const logo = brand!.querySelector("[aria-label='Fusion logo']");
|
||||
expect(logo).not.toBeNull();
|
||||
// Brand container should contain the heading
|
||||
const h1 = brand!.querySelector("h1.logo");
|
||||
expect(h1).not.toBeNull();
|
||||
expect(h1!.textContent).toBe("Fusion");
|
||||
// Logo should appear before the heading within the brand container
|
||||
expect(logo!.compareDocumentPosition(h1!) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
||||
});
|
||||
|
||||
it("renders the settings button", () => {
|
||||
const onOpen = vi.fn();
|
||||
render(<Header onOpenSettings={onOpen} />);
|
||||
|
||||
@@ -163,6 +163,11 @@ body {
|
||||
align-items: center;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
.header-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
.header-logo {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
|
||||
Reference in New Issue
Block a user