Install
Published to GitHub Packages, so your package manager needs to know where the scope lives and needs a token to get in. After that it installs in seconds. No performance anxiety.
1. Tell bun where the scope lives, and how to get in
.npmrc@joshicodes:registry=https://npm.pkg.github.com //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}2. Add the package
terminalbun add @joshicodes/cock3. Import the tokens after Tailwind
app.css@import 'tailwindcss'; @import '@joshicodes/cock/theme.css';4. Use it. Responsibly.
+page.svelteimport { Button, Card, toast } from '@joshicodes/cock';
GITHUB_TOKEN is a personal access token with the read:packages scope. Always use protection: keep it in an environment variable, never in the repository.
Theme
Three colours are set by hand. Surfaces, borders and secondary text are mixed from them with color-mix, so changing the background is enough to re-skin everything. It looks good in anything.
Surfaces
Background
--ck-bg
Surface
--ck-surface
Muted
--ck-muted
Border
--ck-border
Foreground
Foreground
--ck-fg
Muted
--ck-fg-muted
Subtle
--ck-fg-subtle
Border strong
--ck-border-strong
Accent & status
Accent
--ck-accent
Success
--ck-success
Warning
--ck-warning
Danger
--ck-danger
Type scale
Radius & elevation
rounded-sm
rounded-md
rounded-lg
rounded-xl
rounded-2xl
rounded-full
shadow-xs
shadow-sm
shadow-md
shadow-lg
Primitives
The small stuff. Every component takes a class prop that is merged with tailwind-merge, so a one-off override does not need a new variant. Small, but they get the job done.
Button variants
Six ways to get pushed.
Sizes, icons and state
Badge
Label things. It helps everyone know where they stand.
Avatar
With or without a picture. No pressure.
Inline marks
Press ⌘ K to open the palette, or Esc if it all gets a bit much.
Install with bun add @joshicodes/cock and import from '@joshicodes/cock'. Say it with confidence.
CopyButton
Copies a value and confirms it in place.
ThemeToggle
Switches light and dark through the theme state and remembers the choice. Some prefer it with the lights off.
Loading
For when it takes a while to get going.
Separator
Sometimes you just need a little space.
Layout
Cards come in parts: header, content and footer. Skip them and set the padding prop when you only need a box. Comes in four positions.
Card
Weekly digest
Goes out every Monday at 8:00.
1,284 subscribers. Last week's issue was opened by 61 percent of them, which is better than most meetings manage.
Feedback on the name
Collected from fourteen standups.
- "Can we not?" — everyone, at first
- "It's growing on me." — most of them, later
- "Bigger than I expected." — the one who read the docs
Card variants
Flat by default. Elevated adds depth, raised lifts when you hover it, flat fills instead of outlining.
Default
A border, nothing else.
Elevated
Sits a little higher than the rest.
Raised
Hover it. It rises to the occasion.
Flat
Filled, no outline.
Empty state
No messages yet
Don't worry, it happens to everyone. Messages will show up here once they arrive.
Forms
Every control works with bind:value or bind:checked. Field connects the label, help text and error message to the control, including the aria attributes. They handle input really well.
Field
Pass the generated id straight through to the control.
Decides the currency at checkout.
Input
Textarea
Checkbox, radio, switch
Checkbox
Radio
Switch
Slider
PinInput
One box per character. Paste and one-time-code autofill land in the right boxes.
TagInput
Enter or a comma adds a tag, Backspace in an empty field removes the last one.
Inside a form
Field, Input, Textarea, Checkbox and Button in a plain <form>. Nothing is intercepted.
Feedback
Alerts sit in the page and stay until something changes. Toasts are for results that arrive after the user has moved on. Nobody likes to be left hanging.
Alert
Scheduled maintenance
Payment received
Storage almost full
Fox spotted near the coop
Progress
Indeterminate
ProgressRing
The round version, for compact spots and dashboards.
Toast
Tooltip
Overlays
Each one renders into <body> and positions itself against its trigger, which is why a scrolling table or an overflow-hidden card never cuts them off. Escape closes, focus goes back where it came from. They always end up on top.
Modal
Drawer
Dropdown menu
Arrow keys move between items, Escape closes.
CommandPalette
Fuzzy search over a list of commands. The shortcut works anywhere on this page.
Popover
A positioned panel. Put whatever you want in it.
Data
Table only styles plain thead and tbody markup. Sorting and selection are left out on purpose: every project wants them slightly differently. Big numbers, no exaggeration.
Stat
Eggs this week
142
Average weight
63 g
Lost to foxes
1
Water left
82%
Sparkline
A trend without a chart library. Stretches to fill its container.
Eggs per day
21
Fox sightings
3 this week
Coop temperature
18 °C
Heatmap
One cell per day, shaded with the accent. With fit it fills the width. Hover a cell for its value.
Table
tone='accent' carries the theme into the header and rows. Click a row to select it.
| Name | Breed | Status | Eggs this week | Last seen | Actions |
|---|---|---|---|---|---|
H Henrietta | Orpington | Laying | 6 | Just now | |
G Gertrude | Sussex | Broody | 1 | 10 min ago | |
M Mabel | Leghorn | Laying | 7 | 1 h ago | |
D Doris | Silkie | Molting | 0 | Under the shed |
Dense and striped
| Day | Nest | Weight |
|---|---|---|
| Monday | Box 1 | 61 g |
| Monday | Box 3 | 64 g |
| Tuesday | Flower bed | 58 g |
| Tuesday | Box 2 | 71 g |
Effects
Small animated pieces for landing pages and empty states. All of them settle on the final text when the visitor prefers reduced motion, and screen readers get the full text instead of the animation. They all know when to stop.
Terminal
Types commands, prints output. Waits until it is on screen before it starts.
$ bun run count-flock Coop 1 ...... 6 hens Coop 2 ...... 6 hens Rooster ...... on the roof again 12 of 12 accounted for
Typewriter
Types, pauses, deletes and moves on. One string types once; an array cycles.
Wake up to fresh eggs, a finished UI, a very loud bird, morning glory
All twelve hens accounted for. Nothing else to do.
Caret
The blinking bar on its own, for prompts and fake inputs. Stands up straight, never gets tired.
Scramble
Settles from random glyphs into the text. Plays when it scrolls into view, on hover, or whenever the text changes.
CountUp
Counts to its value once it is on screen, and from the old value to the new one after that.
Eggs since March
12,840
Mornings he was on time
99.95%
Odometer
Each digit rolls to its new value, like a mechanical counter.
People who giggled at the package name
1,284
Marquee
Scrolls its content in a loop and pauses on hover. Fills the width however short the content is. It just keeps going.
Highlight
A marker stroke that draws itself once the text is on screen.
Size isn't everything. It's what you do with it that counts.
confetti()
A function, not a component. Bursts from an element or a point and cleans up after itself.
Reveal
Fades content in as it scrolls into view. Anything already visible on load is left alone.
Step 1
Foreplay
Step 2
Build
Step 3
Ship