Skip to content
81 changes: 81 additions & 0 deletions src/Navbar/Navbar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import type { Meta } from "@storybook/react";

import { Navbar, NavbarBrand, NavbarMenu } from ".";

const meta = {
title: "Atoms/Navbar",
component: Navbar,
parameters: {
layout: "fullscreen",
},
} satisfies Meta<typeof Navbar>;

export default meta;

export const All = () => (
<div
style={{
padding: "1rem",
display: "flex",
flexDirection: "column",
gap: "1rem",
}}
>
<Navbar color="white">
<NavbarBrand>
<a href="#" style={{ textDecoration: "none" }}>
White
</a>
</NavbarBrand>
<NavbarMenu>
<a href="#" style={{ textDecoration: "none" }}>
One
</a>
<a href="#" style={{ textDecoration: "none" }}>
Two
</a>
<a href="#" style={{ textDecoration: "none" }}>
Three
</a>
</NavbarMenu>
</Navbar>

<Navbar color="black">
<NavbarBrand>
<a href="#" style={{ textDecoration: "none" }}>
Black
</a>
</NavbarBrand>
<NavbarMenu>
<a href="#" style={{ textDecoration: "none" }}>
One
</a>
<a href="#" style={{ textDecoration: "none" }}>
Two
</a>
<a href="#" style={{ textDecoration: "none" }}>
Three
</a>
</NavbarMenu>
</Navbar>

<Navbar color="gray">
<NavbarBrand>
<a href="#" style={{ textDecoration: "none" }}>
Gray
</a>
</NavbarBrand>
<NavbarMenu>
<a href="#" style={{ textDecoration: "none" }}>
One
</a>
<a href="#" style={{ textDecoration: "none" }}>
Two
</a>
<a href="#" style={{ textDecoration: "none" }}>
Three
</a>
</NavbarMenu>
</Navbar>
</div>
);
61 changes: 61 additions & 0 deletions src/Navbar/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
@use "sass:map";

@use "../sass/mixins";
@use "../sass/tokens";

.navbar {
top: 0;

display: flex;
align-items: center;

font-family: map.get(tokens.$fonts, sans);

.navbar--fixed {
position: fixed;
}

.navbar--container {
margin: auto;
display: flex;
justify-content: space-between;
width: 80%; // TODO: make this responsive

.navbar--brand {
display: flex;
align-items: center;
gap: 1rem; // TODO: make this responsive
}

.navbar--menu {
display: flex;
align-items: center;
gap: 1rem; // TODO: make this responsive
}
}

@each $color,
$scheme
in (
"black": (
"bg": black,
"text": white,
),
"white": (
"bg": white,
"text": black,
),
"gray": (
"bg": gray,
"text": white,
)
)
{
&.navbar--#{$color} {
background-color: map.get(tokens.$brand, map.get($scheme, bg));
* {
color: map.get(tokens.$brand, map.get($scheme, text));
}
}
}
}
46 changes: 46 additions & 0 deletions src/Navbar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { FC, PropsWithChildren } from "react";

import "./index.scss";
import clsx from "clsx";

export type NavbarColor = "black" | "white" | "gray";

export interface NavbarProps {
/**
* Whether the navbar stays in place upon scroll.
*/
fixed?: boolean;

/**
* The navbar's basic color scheme.
*/
color: NavbarColor;
}

/**
* A standard navbar at the top of the page.
*/
export const Navbar: FC<PropsWithChildren<NavbarProps>> = ({
children,
fixed = false,
color,
}) => (
<nav
className={clsx("navbar", {
"navbar--fixed": fixed,
"navbar--white": color === "white",
"navbar--black": color === "black",
"navbar--gray": color === "gray",
})}
>
<div className="navbar--container">{children}</div>
</nav>
);

export const NavbarBrand: FC<PropsWithChildren> = ({ children }) => (
<div className="navbar--brand">{children}</div>
);

export const NavbarMenu: FC<PropsWithChildren> = ({ children }) => (
<menu className="navbar--menu">{children}</menu>
);
5 changes: 5 additions & 0 deletions src/sass/_mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

@use "tokens";

// A helper to sprinkle around during development. Just don't commit it in actual components!
@mixin dev {
outline: 1.5px solid red;
}

@mixin light-mode {
@media (prefers-color-scheme: light) {
@content;
Expand Down
Loading