Karakabakov Contact
Design

White, rounded and quiet.

The look was made for capta.cam, one of the apps, and the rest of the studio took it. Everything below is the live system, rendered from the same stylesheet the rest of the pages use - so when one of them changes, this page has already changed with it.

01

One accent. If it is indigo, you can press it or it is telling you where you are.

02

Objects are fills, not outlines. A tile is soft grey on white, or white on a shadow.

03

Colour belongs to the products. Each app brings its own tint; the site stays out of the way.

I The surface

White, soft grey, and some light

The page is --bg. A band or a tile that needs to separate from it is --soft. Behind a section there can be two large, faint radial gradients, one from each side: they are gradients and not blurred elements, because a half-viewport box under filter: blur() is a full-screen compositing layer, and a gradient that fades to transparent is the same picture for nothing.

A tile on the soft band is white, on the large shadow.

And the dark tile closes a page.

II Colour

Six tokens, and the colours that are not ours

Every ratio below is computed from the hex beside it at build time. The palette itself is imported from src/data/palette.mjs, the same literal tailwind.config.mjs reads - this page holds no copy of it.

The tokens

paper #FFFFFF

The ground. The name is from the look before this one; base.css says why it stayed.

--bg, --paper · ink reads 16.8:1

sheet #F5F5F7

A tile sitting on the ground, and the alternate section band.

--soft, --sheet · ink reads 15.5:1

ink #1D1D1F

Headings and strong text. Never a fill, except the code slab.

--ink · on paper 16.8:1

mute #6E6E73

Body copy. The one token tuned to a threshold rather than a look.

--ink-2, --mute · on paper 5.1:1 · on sheet 4.7:1

accent #5B57E6

Links, buttons, kickers, the current thing. The only colour with a job.

--accent · on paper 5.3:1 · white on it 5.3:1

line #E8E8ED

Hairlines: table rows, dividers, the nav's edge once you scroll.

--line · 1px, never heavier

The apps' tints

Read from projects.ts, where each was taken from the product's own site. A tile is washed in its app's tint at about a tenth strength, and the six together, blurred, are the light behind the pile of prints on the home page.

Folio #31583F Retriever #3563F0 Cleat #E0623F Small Skies #4FB0F0 Capta #5B57E6 Sunflower #F0B429 Cadence #3DBA84 Sleep Timer #7A86F5
III Typography

One face, four weights

Inter, variable, self-hosted. JetBrains Mono is still loaded and sets code and nothing else; it was the body face of every page until this look, and the day it stopped being a label face is the day code started standing out again.

Software for people

Inter 700 Headings, and the name on a tile
Inter 600 Buttons, kickers, labels, the current nav item
Inter 500 Nav links
Inter 400 Body copy

Body copy is --ink-2 at 17px on a 1.6 line-height. Longform in prose.css opens that to 1.72, and a lesson darkens it, because twelve hundred words beside a terminal is a different kind of reading.

IV Emphasis

The coloured word, the kicker, the link

One word in a heading can carry colour: a gradient clipped to the glyphs, solid accent where background-clip: text is missing. The class is still .hl, from when it was a highlighter stripe behind the word.

.hl

.hl-violet

.hl-coral

.eyebrow, above a heading

.underscore, a link in a sentence

.s-badge

V Tiles

The one object

Everything that needs to read as a thing is a rounded tile at --radius. The older class names are from the paper look and were kept because nearly forty files use them; new work should reach for .s-tile.

.panel, .block-soft

Soft grey, no shadow. Sits on the page.

.block-hard

White on --shadow. Floats above it.

.block-card

A link. White on --shadow-sm, and it lifts under the pointer.

.s-tile, tinted

An app's tile

Washed in the product's own colour.

.s-lilac

A course

One of two fixed washes.

.s-mint

The other course

So neighbours never match.

VI Controls

Pills

A button is a pill. The solid one is the accent, for the one thing a view most wants pressed; the quiet one is a wash of the same colour. Skill chips fill with their level under the pointer.

VII Motion

Arrive once, then hold still

Content reveals with .reveal, a view() timeline with no JavaScript; where scroll-driven animation is unsupported the keyframes run for zero seconds and land on the visible frame, so unsupported means already revealed and never invisible. Tiles lift two or three pixels on --ease. Only the games on the home page use --spring, which overshoots: tools sit still, toys do not. Everything that moves stops under prefers-reduced-motion.

VIII Generated art

One object, and a lot of air

Every cover, card and share image is drawn by a script and committed; the tooling that draws them is not. A picture is one object: the thing you would put on the app's icon. It sits on one of two grounds. Dark is near-black with stepped rings of light, a glass tile and a glowing glyph, with colour spilling out from behind the tile. Pastel is a pale field deepening into the product's own colour, with one frosted object floating on a soft shadow. A set mixes the two, so a row of cards is never six black rectangles. There are no outlines anywhere, and nothing is random: the same input draws the same bytes. scripts/art-kit.mjs is the whole vocabulary, and its header has the two attempts that came before this one.

Three sliders on a soft violet field, each left at a different setting: one high, one in the middle, one low.Three sheets stacked as layers, the top one brightest, glowing on a dark glass tile with warm orange and pink light behind it.A three-by-three sheet of frosted rounded squares on a soft indigo field, the centre one filled in indigo.A frosted glass pill on a soft periwinkle field, holding a countdown ring with less than a third of it left.
scripts/product-cards.mjs images/products/<app>-card.webp

One object per app: the pile on the home page and the corner of each tile.

scripts/post-heroes.mjs post heroes

One object per post. Wordless: the alt text carries the meaning, so a reworded sentence never costs a re-render.

scripts/og-card.mjs og-default.png

The share card every page falls back to.

scripts/og-product-cards.mjs og-<app>.png

One share card per product page, carrying the same glyph as the app's tile.

scripts/course-card.mjs course covers

The title, the lesson count in a pill, and the count again as the glyph.

scripts/lesson-cards.mjs lesson covers

Titles read straight out of the lesson frontmatter. A title that does not fit fails the run.

scripts/x-banner.mjs images/x-banner.png

Laid out by hand, because the avatar covers the corner a cover puts its address in.

scripts/favicon.mjs favicon.png, apple-touch-icon.png

The mark, at the sizes Google and Apple ask for.

IX Not this

What would drag it back

Each of these is something the site did while it was paper, and did well. They are listed because they are habits in this codebase, not because they are wrong.

Borders that draw the object

A fill, a radius, sometimes a soft shadow

An outline says diagram. A tile is a surface, and a surface has no edge line.

Hard offset shadows

Two blurred ones, small and large

An offset says block print. Here a thing either sits on the page or floats a little above it.

Uppercase, letter-spaced labels

Sentence case at 600 in the accent

Tracked capitals at 11px were the mono look's voice. In Inter they read as a form.

Ink-filled tabs and strips

A chip in a wash of the accent

A black bar across a rounded white tile is the heaviest thing on the screen and says the least.

The mono as a label face

The mono for code, only

When every label is mono, code stops looking like code.

A class called .btn, .badge, .card or .hero

An s- prefix

daisyUI is still a Tailwind plugin here and owns those names. It has cost a morning before.