Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ pnpm-lock.yaml
# Generated files
src/routeTree.gen.ts
src/api/generated

# Design reference (hand-authored, includes embedded base64 fonts)
docs/
421 changes: 421 additions & 0 deletions docs/criticalbit-logo-r3.html

Large diffs are not rendered by default.

55 changes: 25 additions & 30 deletions public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/fonts/GeistPixel-Grid.woff2
Binary file not shown.
Binary file removed public/fonts/GeistPixel-Line.woff2
Binary file not shown.
20 changes: 20 additions & 0 deletions src/components/brand-lockup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { RingBurstIcon } from "@/components/ring-burst-icon"
import { Wordmark } from "@/components/wordmark"
import { cn } from "@/lib/utils"

type BrandLockupProps = {
className?: string
}

/**
* Ring burst + wordmark, sized for the navbar. Shared by the navbar and the
* loading skeleton so the two can never drift out of sync.
*/
export function BrandLockup({ className }: BrandLockupProps) {
return (
<span className={cn("inline-flex items-center gap-2", className)}>
<RingBurstIcon className="text-primary size-7 shrink-0" />
<Wordmark className="text-lg" showTld={false} />
</span>
)
}
6 changes: 4 additions & 2 deletions src/components/navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ChevronDown, ExternalLink, LogOut } from "lucide-react"
import { Link } from "@tanstack/react-router"
import { BrandLockup } from "@/components/brand-lockup"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
Expand All @@ -20,9 +21,10 @@ export function Navbar() {
<div className="flex h-14 items-center justify-between px-4">
<Link
to="/"
className="font-pixel hover:text-primary text-lg tracking-wide transition-colors"
aria-label="criticalbit.gg"
className="transition-opacity hover:opacity-80"
>
CriticalBit
<BrandLockup />
</Link>

<div className="flex items-center gap-2">
Expand Down
53 changes: 53 additions & 0 deletions src/components/ring-burst-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
type RingBurstIconProps = {
className?: string
}

// 11×11 pixel grid — the "ring burst" mark from docs/criticalbit-logo-r3.html.
// 1 = filled cell; eight rays radiate from a hollow core.
const RING_GRID = [
[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0],
[0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1],
[0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0],
[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
] as const

// Each cell sits in a CELL-wide slot; GAP keeps a crisp pixel seam between them.
const CELL = 4
const GAP = 1
const SPAN = RING_GRID.length * CELL - GAP // 11 × 4 − 1 = 43

const RING_CELLS = RING_GRID.flatMap((row, r) =>
row.flatMap((filled, c) => (filled ? [{ x: c * CELL, y: r * CELL }] : []))
)

/**
* The criticalbit ring burst. Renders as `currentColor`, so set the colour with
* a `text-*` utility and the size with `size-*` (or any width/height class).
*/
export function RingBurstIcon({ className }: RingBurstIconProps) {
return (
<svg
viewBox={`0 0 ${SPAN} ${SPAN}`}
fill="currentColor"
aria-hidden="true"
className={className}
>
{RING_CELLS.map(({ x, y }) => (
<rect
key={`${x}-${y}`}
x={x}
y={y}
width={CELL - GAP}
height={CELL - GAP}
/>
))}
</svg>
)
}
29 changes: 29 additions & 0 deletions src/components/wordmark.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { cn } from "@/lib/utils"

type WordmarkProps = {
className?: string
/** Render the trailing ".gg". Turn off for compact placements like the navbar. */
showTld?: boolean
}

/**
* The split wordmark: "critical" in Geist Sans, "BIT" in Geist Pixel Grid, and a
* trailing ".gg". Every part is sized in `em`, so the whole lockup scales with
* the inherited font size — set a `text-*` utility on the parent or via
* `className` to resize it.
*/
export function Wordmark({ className, showTld = true }: WordmarkProps) {
return (
<span className={cn("inline-flex items-baseline font-sans", className)}>
<span className="text-foreground font-normal tracking-[-0.01em]">
critical
</span>
<span className="font-pixel text-primary pl-[0.14em] text-[0.85em] tracking-[0.05em]">
BIT
</span>
{showTld && (
<span className="text-muted-foreground text-[0.4em]">.gg</span>
)}
</span>
)
}
12 changes: 3 additions & 9 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
@custom-variant dark (&:is(.dark *));

@font-face {
font-family: "Geist Pixel Line";
src: url("/fonts/GeistPixel-Line.woff2") format("woff2");
font-family: "Geist Pixel Grid";
src: url("/fonts/GeistPixel-Grid.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
Expand All @@ -15,7 +15,7 @@
:root {
--radius: 0.625rem;
--font-sans: "Geist Variable", ui-sans-serif, system-ui, sans-serif;
--font-pixel: "Geist Pixel Line", monospace;
--font-pixel: "Geist Pixel Grid", monospace;

--background: oklch(0.97 0.01 230);
--foreground: oklch(0.22 0.02 260);
Expand All @@ -36,8 +36,6 @@
--border: oklch(0.9 0.01 230);
--input: oklch(0.9 0.01 230);
--ring: oklch(0.88 0.08 210);
--brand-emerald: oklch(0.85 0.11 165);
--brand-emerald-foreground: oklch(0.18 0.015 270);
--chart-1: oklch(0.88 0.08 210);
--chart-2: oklch(0.75 0.17 165);
--chart-3: oklch(0.75 0.15 75);
Expand Down Expand Up @@ -74,8 +72,6 @@
--border: oklch(0.28 0.02 270);
--input: oklch(0.28 0.02 270);
--ring: oklch(0.82 0.11 220);
--brand-emerald: oklch(0.75 0.17 165);
--brand-emerald-foreground: oklch(0.18 0.015 270);
--chart-1: oklch(0.82 0.11 220);
--chart-2: oklch(0.75 0.17 165);
--chart-3: oklch(0.8 0.12 80);
Expand Down Expand Up @@ -113,8 +109,6 @@
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-brand-emerald: var(--brand-emerald);
--color-brand-emerald-foreground: var(--brand-emerald-foreground);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
Expand Down
3 changes: 2 additions & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { createRoot } from "react-dom/client"
import { reactErrorHandler } from "@sentry/react"
// @ts-expect-error -- fontsource CSS-only import, no types
import "@fontsource-variable/geist"
import { BrandLockup } from "./components/brand-lockup"
import { ErrorBoundary } from "./components/error-boundary"
import { NotFound } from "./components/not-found"
import { ThemeProvider } from "./components/theme-provider"
Expand Down Expand Up @@ -39,7 +40,7 @@ function AppShellSkeleton() {
<>
<nav className="border-border/50 bg-background/80 fixed top-0 z-50 w-full border-b backdrop-blur-sm">
<div className="flex h-14 items-center justify-between px-4">
<span className="font-pixel text-lg tracking-wide">CriticalBit</span>
<BrandLockup />
<div className="flex items-center gap-2">
<Skeleton className="size-8 rounded-md" />
<Skeleton className="h-8 w-20 rounded-md" />
Expand Down
10 changes: 10 additions & 0 deletions src/pages/home/crt.css
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,15 @@
}
}

/* Icon counterpart to .crt-glow — drop-shadow reaches SVG shapes that
text-shadow cannot. */
.crt-glow-icon {
color: var(--primary);
filter: drop-shadow(0 0 14px var(--primary))
drop-shadow(0 0 36px color-mix(in oklch, var(--primary) 50%, transparent));
animation: crt-glow-pulse 3s ease-in-out infinite;
}

/* Glass reflection highlight — top-left gleam */
.crt-screen::before {
content: "";
Expand Down Expand Up @@ -405,6 +414,7 @@
.crt-tube,
.crt-screen::after,
.crt-glow,
.crt-glow-icon,
.crt-static,
.crt-menu-row--active .crt-menu-cursor {
animation: none;
Expand Down
2 changes: 1 addition & 1 deletion src/pages/home/home-page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe("HomePage", () => {
it("renders the site name", async () => {
await renderWithFileRoutes(<></>)
expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent(
"CriticalBit"
/criticalbit\.gg/i
)
})
})
8 changes: 5 additions & 3 deletions src/pages/home/home-page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { useRef, type KeyboardEvent } from "react"
import { RingBurstIcon } from "@/components/ring-burst-icon"
import { Wordmark } from "@/components/wordmark"
import "./crt.css"

export function HomePage() {
Expand Down Expand Up @@ -31,9 +33,9 @@ export function HomePage() {
<div className="crt-curvature pointer-events-none absolute inset-0" />
<div className="crt-static pointer-events-none absolute inset-0" />
<div className="crt-content relative text-center">
<h1 className="crt-glow font-pixel text-6xl tracking-wide sm:text-8xl">
<span className="text-white">Critical</span>
<span>Bit</span>
<RingBurstIcon className="crt-glow-icon mx-auto mb-4 block size-16 sm:size-24" />
<h1 className="crt-glow text-4xl sm:text-7xl">
<Wordmark />
</h1>
<p className="text-muted-foreground mt-4 text-lg">
Community gaming tools for the games you love.
Expand Down
Loading