PUDGE/UI v0.2.1 2025-07
  • + 5 new hardware compositions: Walkman Deck (Sony WM-D6C), Game Boy (Nintendo DMG-01), DJ Controller Deck (Pioneer DDJ), Synthesizer Panel (Roland SH-101 / Moog Minimoog)
  • + Changelog page at /changelog with full version history
  • + Animated NEW badge on recently added specs and compositions
  • + LCD-style version update banner with per-version dismiss
dials .dial-h-body

Cylindrical Dial (Horizontal)

DSLR front/rear command dial — horizontal barrel with diamond knurl

Size
Material
EXPOSURE COMP
+0.7
<div class="dial-h">
  <div class="dial-h-label">EXPOSURE COMP</div>
  <div class="dial-h-body">
    <div class="knurl-cross"></div>
    <div class="dial-cap"></div>
    <div class="dial-badge">+0.7</div>
  </div>
</div>
.dial-h { display: flex; flex-direction: column; align-items: center; gap: 6px; width: 100%; max-width: 190px; }
.dial-h-label { font-family: var(--font-ui); font-size: 7px; font-weight: 700; letter-spacing: 2px; color: var(--text-muted); text-transform: uppercase; }

.dial-h-body {
  width: 168px; height: 44px; border-radius: 6px;
  position: relative; cursor: ew-resize; user-select: none;
  background: linear-gradient(180deg, #4a4845, #2a2826);
  border: 1px solid var(--border-mid);
  box-shadow: 0 2px 8px rgba(0,0,0,0.3); overflow: hidden;
}
[data-theme="light"] .dial-h-body { background: linear-gradient(180deg, #d0cec8, #b0aea8); border-color: #aaa; }

.dial-h-body .knurl-cross {
  position: absolute; inset: 0; pointer-events: none;
  background-image:
    repeating-linear-gradient(75deg, transparent 0px, transparent 2px, rgba(0,0,0,0.06) 2px, rgba(0,0,0,0.06) 3px, transparent 3px, transparent 6px),
    repeating-linear-gradient(-75deg, transparent 0px, transparent 2px, rgba(0,0,0,0.06) 2px, rgba(0,0,0,0.06) 3px, transparent 3px, transparent 6px);
}

.dial-h-body .dial-cap {
  position: absolute; top: 0; left: 0; right: 0; height: 13px;
  background: linear-gradient(180deg, rgba(255,255,255,0.12) 0%, transparent 100%);
  border-bottom: 1px solid rgba(0,0,0,0.12); pointer-events: none;
  border-radius: 6px 6px 0 0;
}

.dial-h-body .dial-badge {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%);
  font-family: var(--font-display); font-size: 11px; letter-spacing: 1px;
  color: var(--text-primary); z-index: 3; pointer-events: none;
  background: var(--bg-surface); padding: 2px 8px; border-radius: 3px;
  border: 1px solid var(--border-subtle);
}

.dial-h-body.variant-silver {
  background: linear-gradient(180deg, #d8d6d0, #b8b6b0);
  border-color: #a8a6a0;
}
.dial-h-body.variant-chrome {
  background: linear-gradient(180deg, #5a5855, #3a3835);
  border-color: #666;
}

API

Class Type Description
.dial-h-body Base Primary component class
.md Size Medium (default) variant
.brushed-metal Material Brushed-metal surface variant
.chrome Material Chrome surface variant

Design Notes

Physical Analog

Reference devices: Nikon D850 front/rear command dials, Sony Alpha front dial, Canon EOS main dial (landscape orientation). Mechanism: Identical encoder mechanism to cylindrical scroll, but oriented horizontally. Main command dial on camera grip, operated by index finger. User rolls barrel forward/backward to change values.

Geometry

Property Value
Barrel 168x44px (wide and short)
Knurl pattern Diamond crosshatch (two sets of diagonal grooves at 60-75 degrees)
Top bevel cap 13px bright-to-transparent gradient
Badge Centered value display

CSS Recipe

Wrapper

.dial-h { display: flex; flex-direction: column; align-items: center; gap: 6px; width: 100%; max-width: 190px; }
.dial-h-label { font-family: var(--font-ui); font-size: 7px; font-weight: 700; letter-spacing: 2px; color: var(--text-muted); text-transform: uppercase; }

Body

.dial-h-body {
  width: 168px; height: 44px; border-radius: 6px;
  position: relative; cursor: ew-resize; user-select: none;
  background: linear-gradient(180deg, #4a4845, #2a2826);
  border: 1px solid var(--border-mid);
  box-shadow: 0 2px 8px rgba(0,0,0,0.3); overflow: hidden;
}
[data-theme="light"] .dial-h-body { background: linear-gradient(180deg, #d0cec8, #b0aea8); border-color: #aaa; }

Diamond Knurl Pattern

.dial-h-body .knurl-cross {
  position: absolute; inset: 0; pointer-events: none;
  background-image:
    repeating-linear-gradient(75deg, transparent 0px, transparent 2px, rgba(0,0,0,0.06) 2px, rgba(0,0,0,0.06) 3px, transparent 3px, transparent 6px),
    repeating-linear-gradient(-75deg, transparent 0px, transparent 2px, rgba(0,0,0,0.06) 2px, rgba(0,0,0,0.06) 3px, transparent 3px, transparent 6px);
}

Top Bevel Cap

.dial-h-body .dial-cap {
  position: absolute; top: 0; left: 0; right: 0; height: 13px;
  background: linear-gradient(180deg, rgba(255,255,255,0.12) 0%, transparent 100%);
  border-bottom: 1px solid rgba(0,0,0,0.12); pointer-events: none;
  border-radius: 6px 6px 0 0;
}

Value Badge

.dial-h-body .dial-badge {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%);
  font-family: var(--font-display); font-size: 11px; letter-spacing: 1px;
  color: var(--text-primary); z-index: 3; pointer-events: none;
  background: var(--bg-surface); padding: 2px 8px; border-radius: 3px;
  border: 1px solid var(--border-subtle);
}

Material Variants

.dial-h-body.variant-silver {
  background: linear-gradient(180deg, #d8d6d0, #b8b6b0);
  border-color: #a8a6a0;
}
.dial-h-body.variant-chrome {
  background: linear-gradient(180deg, #5a5855, #3a3835);
  border-color: #666;
}

HTML Structure

<div class="dial-h">
  <div class="dial-h-label">EXPOSURE COMP</div>
  <div class="dial-h-body">
    <div class="knurl-cross"></div>
    <div class="dial-cap"></div>
    <div class="dial-badge">+0.7</div>
  </div>
</div>

Size Variants

No explicit size variants. Fixed at 168x44px.

Material Variants

Variant Background Border
default linear-gradient(180deg, #4a4845, #2a2826) var(--border-mid)
.variant-silver linear-gradient(180deg, #d8d6d0, #b8b6b0) #a8a6a0
.variant-chrome linear-gradient(180deg, #5a5855, #3a3835) #666

Theme Behavior

  • Dark default: dark metallic gradient
  • Light default: light metallic gradient (#d0cec8 to #b0aea8)
  • Silver and chrome variants are fixed (metal doesn't change with theme)
  • Badge uses surface tokens for adaptive background

Constraints

  1. Diamond knurl MUST use two overlapping repeating-linear-gradient at +75deg and -75deg.
  2. Top bevel cap provides the bright line where barrel enters camera body.
  3. Cursor MUST be ew-resize (horizontal scroll interaction).
  4. Badge is centered and positioned above the knurl texture (z-index: 3).

Accessibility

  • Add tabindex="0" and role="slider"
  • Keyboard: Arrow Left/Right to adjust value
  • Requires JS for interaction
♿ Accessibility
Element
Use semantic HTML with appropriate ARIA roles
Keyboard
Arrow keys to adjust value
Focus
Visible focus indicator required. Use native browser focus ring or custom :focus-visible styles.

Hand it to your agent

pudge-ui is a spec, not a package — there's nothing to npm install. Pick your stack, copy the prompt, and your coding agent builds this component from the spec.

—
—

Rotary encoders have infinite rotation. Mode dials have fixed stops. Different physics.

Depth model →
Pudge