[ BEBEN_CORE ] V 2.0.0 — UPDATED APRIL 2026

Design
System
Spec.

The technical and philosophical foundation behind every Beben Design deliverable. This document defines the decisions — not just the values — so the reasoning travels with the code.

v1.0.0 Initial spec. Roboto Mono, pure black/white, FFC917 yellow.
v2.0.0 JetBrains Mono, Smokey White/Onyx palette, E71D36 red accent, fixed nav system, motion spec added.

Type Logic

A single typeface family — JetBrains Mono — handles all weight classes from 100 to 800. Monospace type is not an aesthetic choice here. It reflects the technical discipline of the studio: precise, structured, and with no ambiguity in letter spacing.

Display clamp(2.5rem → 4.5rem)
weight 800
tracking -0.04em
BEBEN
Page titles
Hero H1
Section 1.0–1.2rem
weight 700
tracking +0.05em
OUR SERVICES
H2 labels
Nav links
Body 0.82–0.9rem
weight 400
leading 1.75–1.8
We shape ideas into clear, usable systems — designed to work long before development begins.
Paragraphs
Descriptions
Label / UI 0.6–0.72rem
weight 700
tracking +0.12em
[ 01 // SECTION_LABEL ]
Tags, labels
Micro copy

Why monospace over a humanist typeface?

Most design studios default to geometric sans-serifs — clean, friendly, forgettable. Monospace forces every character to occupy identical horizontal space, which produces a grid-aligned, technical cadence. It signals precision without saying it.

Business read: clients hiring Beben for HMI, dashboard, or technical product work see a studio that operates in the same visual register as their own engineering tools. Typeface choice is positioning.

Why clamp() for display sizes?

Fluid type scaling eliminates the need for multiple breakpoint overrides. A single clamp(2.5rem, 7vw, 4.5rem) declaration handles every viewport from 320px to 4K. Less code, fewer edge cases, identical visual weight at all sizes.

Business read: fast, maintainable front-end code is part of the product quality promise — the type system is a direct demonstration of that practice.

Palette v2.0

The palette moved away from pure #000000/#ffffff in v2.0. Smokey White and Onyx are warmer, less harsh on extended reading sessions, and carry a material quality that pure web defaults lack. The accent shifted from yellow to red — a deliberate reversal of the studio's earlier identity.

Smokey White
#F7F4F3
Light bg, card surfaces
Onyx
#141414
Light text, dark bg, nav
Signal Red
#E71D36
CTAs, highlights, active states
Strobe Yellow
#FFC917
Tags, labels, secondary accent
Nav Onyx
#141414 @ 80%
Fixed nav — both themes
Muted
#5A5A5A / #999
Body copy, secondary text

Why Signal Red (#E71D36) replaced Strobe Yellow as the primary accent?

Yellow is high-visibility but carries connotations of caution, construction, and warning systems — useful for HMI work, less useful as a brand action colour. Red (#E71D36) is assertive without being aggressive. It creates a stronger visual hierarchy on both Smokey White and Onyx surfaces, passes WCAG AA contrast requirements, and reads at a distance on marketing materials.

Business read: red is associated with urgency and confidence — appropriate for a studio asking clients to commit budget to a decision. Yellow remains in the system as a secondary accent for labels and tags, where its high-visibility character is an advantage.

Why Smokey White and Onyx instead of #fff and #000?

Pure white (#ffffff) creates maximum contrast that strains the eye over long sessions. #F7F4F3 reduces that harshness by approximately 3% luminance — imperceptible as a change but meaningful over a full page read. Onyx (#141414) does the same from the dark end: not quite black, which gives dark mode surfaces a material warmth rather than a void.

Business read: warmer surfaces are associated with craft and considered production. The detail signals that the studio made an intentional choice, not a default one — which is exactly the positioning Beben Design needs.

Why is the nav always dark (#141414CC) regardless of theme?

A nav that flips with the theme introduces a cognitive context-switch on every page load: the user must re-orient to a differently coloured chrome. A fixed dark nav with consistent --nav-text: #F7F4F3 content provides a stable anchor at the top of every page. The 80% opacity allows the body content to ghost through on scroll, maintaining spatial depth without obscuring readability.

Business read: consistent navigation reduces user uncertainty. In UX terms, predictable chrome is invisible chrome — which is the goal.

Spatial Logic

All spacing derives from an 8px base unit. Section padding runs at 80px vertical. The content column caps at 1100px with 2rem (32px) gutters on each side.

// BASE UNIT

8px / 0.5rem

All spacing values are multiples of 8: 8, 16, 24, 32, 40, 80px. This keeps layout relationships mathematically consistent across breakpoints without requiring a framework.

// CONTENT MAX-WIDTH

1100px

Chosen to be wide enough for two-column layouts at desktop scale but narrow enough to prevent line lengths exceeding ~90 characters on body text — the threshold where readability degrades.

// SECTION RHYTHM

80px vertical

Consistent top/bottom padding on every section. Sections are separated by a 1px var(--dot) line, not whitespace alone — creating a document-like structure that reads as systematic rather than decorative.

// DOT GRID

24px × 24px

Background texture generated entirely in CSS via radial-gradient. Zero asset requests. The 24px pitch aligns to the 8px base unit (24 = 8 × 3), so the grid never conflicts with layout elements positioned on the spacing scale.

Why not use a CSS framework for the grid?

Frameworks add abstraction layers that obscure layout decisions. Writing spacing explicitly in CSS means every value is a deliberate choice, not a side effect of a utility class collision. The output is leaner, faster, and fully auditable.

Business read: clients inheriting a Beben codebase get CSS they can read and maintain. No dependency on a third-party framework that may deprecate or change its API.

Interaction Timing

Motion is purposeful or absent. Decorative animation is a performance and maintenance liability. Every transition in the system has a defined purpose, duration, and easing.

Token Value Easing Usage
Reveal 0.6s ease Sections entering viewport — opacity 0→1, translateY 24px→0
Hover state 0.2s ease Card backgrounds, link colours, button fills
Theme switch 0.3s ease background-color and color on body — no flash
Underline CTA 0.25s ease width 0→100% on ::after pseudo-element
Menu overlay 0.35s ease Mobile nav opacity + visibility — prevents layout flash
Toast 0.3s ease opacity + translateY — enters from below, auto-dismisses at 2.5s
Hamburger → × 0.3s ease transform on three spans — rotate and scale, no JS class swap on individual spans

Why visibility + opacity for the mobile overlay instead of display:none toggle?

display:none removes the element from the paint tree instantly — no CSS transition can fire on it. Using visibility: hidden combined with opacity: 0 keeps the element in the paint tree but makes it invisible and non-interactive. The transition fires on both properties, producing a proper fade in/out.

Business read: a mobile nav that flashes open without animation reads as broken to users. It signals low build quality before a single word of content is read.

Interaction Patterns

Every interactive component follows a single rule: the hover state must invert or visibly shift — not merely change opacity. A card that goes from Smokey White to Onyx on hover communicates state change unambiguously.

// CARDS

Border-first, Fill-on-hover

Cards use a 1px var(--border) frame with transparent fill at rest. On hover, background fills to var(--text). The inversion is legible in both themes without conditional logic.

// CTA LINKS

Inline + Underline Grow

display: inline ensures the hover target matches only the text width — not the full container width. A ::after pseudo-element grows from 0 to 100% width on hover, replacing the need for a traditional underline.

// FAQ ACCORDION

max-height CSS Transition

Answers expand via max-height: 0 → 300px with overflow: hidden. No JavaScript height calculation. The + rotates 45° to form × via transform: rotate(45deg). One button per item, aria-expanded managed in JS.

// TOOLTIPS

Opacity + Transform Pair

Tooltips use opacity: 0 → 1 combined with translateY(4px → 0). Positioned relative to the parent's inline text width — never the container. Disabled on touch devices via @media (max-width: 480px).

// PROCESS STEPS

Grid + Hover Indent

Two-column grid: fixed 60px number column, fluid content column. On hover, padding-left shifts by 8px — a micro-interaction that implies forward momentum without animation overhead.

// TOAST NOTIFICATIONS

Fixed + Auto-dismiss

Fixed position at bottom: 2rem, centred via translateX(-50%). Enters with translateY from below. Auto-dismisses after 2.5s. Uses --text background and --bg text — the inversion of the page itself.

See the system
in production.

Every rule in this document is applied live across beben.design. No gap between the spec and the shipped product — because the spec was written from the product, not before it.

VISIT BEBEN.DESIGN ↗