Files
tiennm99 ac08938ed6 polish: apply ui/ux review top-5 changes
1. Header declutter — move daily badge + practice controls out of the
   busy header into the .target section so the puzzle-number / guess
   counter / streak read as a single hierarchy line.

2. Channel-chip contrast — darken --c-red from #d64545 to #cc3838 for
   WCAG AA on white text. Drop the 0.85 opacity on .channel-range and
   bump its font size 0.68→0.72rem; was under AA on colored chips.

3. 44px touch targets — bump padding on .hints-toggle, .share-btn,
   .mode-switch button, .poss-apply. Grow .help-chip 16→24px with a
   4px invisible hit-area ring.

4. Hints-toggle collapsed state — add .possible.collapsed that strips
   the card chrome when hints are off, so the toggle reads as a
   standalone pill instead of an empty bordered container.

5. Screen reader polish — tabular-nums on mutating numeric text,
   aria-live=polite on #result, aria-invalid synced with the .invalid
   class on RGB inputs.
2026-04-24 11:39:47 +07:00

772 lines
19 KiB
CSS
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ============================================================
Colordle style.css
Dark theme, custom properties, accessibility, animations.
============================================================ */
/* ── Custom properties ─────────────────────────────────────── */
:root {
--bg: #161616;
--bg-elev-1: #1d1d1d;
--bg-elev-2: #262626;
--bg-input: #121212;
--border: #2a2a2a;
--border-strong:#3d3d3d;
--text: #e8e8e8;
--text-dim: #9e9e9e;
--c-red: #cc3838; /* darker for WCAG AA on white text */
--c-yellow: #e2b93b;
--c-green: #46b04a;
--c-exact: #1fd17a;
/* Channel-axis tints */
--ch-r: #ff6b6b;
--ch-g: #5ad884;
--ch-b: #6fa0ff;
--radius: 8px;
--radius-sm: 6px;
--focus-ring: 0 0 0 2px #1fd17a;
}
/* ── Reset / base ───────────────────────────────────────────── */
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
margin: 0;
padding: 1rem;
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.4;
}
main {
max-width: 640px;
margin: 0 auto;
}
/* ── Header ─────────────────────────────────────────────────── */
header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
flex-wrap: wrap;
margin-bottom: 1.25rem;
}
h1 {
margin: 0;
font-size: 1.75rem;
letter-spacing: 0.02em;
display: flex;
align-items: center;
gap: 0.5rem;
}
.game-icon {
width: 1.8em;
height: 1.8em;
object-fit: contain;
display: inline-block;
}
.game-icon:not([src]), .game-icon[src=""] { display: none; }
/* ── Controls ───────────────────────────────────────────────── */
.controls {
display: flex;
align-items: center;
gap: 0.75rem;
font-size: 0.9rem;
}
.controls label {
display: flex;
align-items: center;
gap: 0.5rem;
opacity: 0.85;
}
/* ── Base form elements ─────────────────────────────────────── */
button,
input[type="number"],
input[type="text"] {
font: inherit;
color: var(--text);
background: var(--bg-elev-2);
border: 1px solid var(--border-strong);
border-radius: var(--radius-sm);
padding: 0.45rem 0.7rem;
}
input[type="number"] { width: 4.5rem; }
input[type="text"] {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
width: 7rem;
}
/* ── RGB channel inputs ─────────────────────────────────────── */
.rgb-group {
display: flex;
gap: 0.25rem;
}
.rgb-group input {
width: 3.2rem;
text-align: center;
padding: 0.45rem 0.35rem;
}
.rgb-group input:nth-child(1) { box-shadow: inset 0 -2px 0 var(--c-red); }
.rgb-group input:nth-child(2) { box-shadow: inset 0 -2px 0 var(--c-green); }
.rgb-group input:nth-child(3) { box-shadow: inset 0 -2px 0 #4d88ff; }
input[type="text"].invalid { border-color: #c44; }
/* ── Buttons ────────────────────────────────────────────────── */
button {
cursor: pointer;
transition: background 0.15s;
}
button:hover:not(:disabled) { background: #333; }
button:disabled { opacity: 0.5; cursor: not-allowed; }
button:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
input:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
/* ── Target swatch ──────────────────────────────────────────── */
.target { text-align: center; margin-bottom: 1.25rem; }
.swatch {
width: 100%;
height: 180px;
border-radius: 10px;
border: 2px solid var(--border);
box-shadow: inset 0 0 0 1px #0006;
display: flex;
align-items: center;
justify-content: center;
font-size: 5rem;
font-weight: 800;
color: #555;
letter-spacing: -0.02em;
user-select: none;
}
.swatch.hidden {
background:
repeating-linear-gradient(
45deg,
#242424 0, #242424 14px,
#2e2e2e 14px, #2e2e2e 28px
) !important;
}
.swatch.small {
width: 44px;
height: 44px;
flex-shrink: 0;
}
.counter {
margin: 0.6rem 0 0;
opacity: 0.8;
font-variant-numeric: tabular-nums;
}
/* Practice-mode controls sit centered under the counter; hidden in daily mode */
.target .controls {
margin-top: 0.5rem;
justify-content: center;
flex-wrap: wrap;
}
/* ── Input section ──────────────────────────────────────────── */
.input {
display: flex;
align-items: center;
gap: 0.6rem;
flex-wrap: wrap;
margin-bottom: 1.25rem;
padding: 0.75rem;
background: var(--bg-elev-1);
border-radius: var(--radius);
border: 1px solid var(--border);
}
.input input[type="color"] {
width: 44px;
height: 44px;
padding: 0;
border: 1px solid var(--border-strong);
border-radius: var(--radius-sm);
background: transparent;
cursor: pointer;
}
.input input[type="color"]:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
/* Hex shortcut input (monospace, uppercase, auto-populated) */
.hex-input {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
text-transform: uppercase;
width: 5.5rem;
text-align: center;
}
.guess-preview {
width: 44px;
height: 44px;
border: 1px solid var(--border-strong);
border-radius: var(--radius-sm);
background: #808080;
}
/* ── Possible-target panel ──────────────────────────────────── */
.possible {
display: flex;
flex-direction: column;
gap: 0.4rem;
margin-bottom: 1rem;
padding: 0.6rem 0.75rem;
background: var(--bg-elev-1);
border: 1px solid var(--border);
border-radius: var(--radius);
font-size: 0.82rem;
transition: background 160ms ease, border-color 160ms ease, padding 160ms ease;
}
/* Collapsed state: strip the card chrome so a single naked pill is shown.
* Prevents the "empty loading card" look that the bordered frame creates
* when the hints body is hidden. */
.possible.collapsed {
background: transparent;
border-color: transparent;
padding: 0;
margin-bottom: 0.75rem;
}
/* Collapsible hints panel — default hidden, toggled via .hints-toggle */
.hints-header {
position: relative;
display: flex;
align-items: center;
gap: 0.5rem;
}
.hints-toggle {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.55rem 0.9rem;
background: var(--bg-elev-2);
border: 1px solid var(--border-strong);
border-radius: var(--radius-sm);
color: var(--text);
font: inherit;
font-size: 0.82rem;
font-weight: 600;
cursor: pointer;
transition: background 0.15s;
}
.hints-toggle:hover { background: #333; }
.hints-toggle[aria-expanded="true"] {
background: var(--bg-elev-1);
border-color: var(--c-exact);
color: var(--c-exact);
}
.hints-toggle-caret {
display: inline-block;
width: 0;
height: 0;
border-left: 5px solid currentColor;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
transition: transform 180ms ease;
}
.hints-toggle[aria-expanded="true"] .hints-toggle-caret {
transform: rotate(90deg);
}
.hints-body {
display: flex;
flex-direction: column;
gap: 0.4rem;
}
.hints-body[hidden] { display: none; }
.poss-row {
display: flex;
align-items: center;
gap: 0.5rem;
font-family: ui-monospace, monospace;
}
.poss-label {
width: 1.1rem;
font-weight: 700;
text-align: center;
}
.poss-row.cr .poss-label { color: var(--ch-r); }
.poss-row.cg .poss-label { color: var(--ch-g); }
.poss-row.cb .poss-label { color: var(--ch-b); }
.poss-track {
position: relative;
flex: 1;
height: 1.4rem;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 4px;
overflow: hidden;
min-width: 0;
}
.poss-seg {
position: absolute;
top: 0;
bottom: 0;
background: #3a3a3a;
}
.poss-row.cr .poss-seg { background: linear-gradient(to right, #4a1d1d, var(--ch-r)); }
.poss-row.cg .poss-seg { background: linear-gradient(to right, #1d4a2a, var(--ch-g)); }
.poss-row.cb .poss-seg { background: linear-gradient(to right, #1d2a4a, var(--ch-b)); }
/* Tick marks on possible-target bars */
.poss-tick {
position: absolute;
top: -1px;
bottom: -1px;
width: 2px;
margin-left: -1px;
background: #fff;
box-shadow: 0 0 0 1px #000a;
pointer-events: auto;
}
.poss-tick.exact { background: var(--c-exact); box-shadow: 0 0 0 1px #000, 0 0 6px var(--c-exact); }
.poss-tick.green { background: var(--c-green); }
.poss-tick.yellow { background: var(--c-yellow); }
.poss-tick.red { background: var(--c-red); }
.poss-hint {
min-width: 4.5rem;
font-size: 0.72rem;
color: #a8a8a8;
text-align: right;
}
.poss-apply {
margin-top: 0.25rem;
display: inline-flex;
align-items: center;
gap: 0.5rem;
align-self: flex-start;
padding: 0.55rem 0.85rem;
background: var(--bg-elev-2);
border: 1px solid var(--border-strong);
border-radius: var(--radius-sm);
font-size: 0.82rem;
color: var(--text);
cursor: pointer;
}
.poss-apply:hover { background: #333; }
.poss-apply:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
.poss-apply-label { opacity: 0.85; }
.poss-apply-swatch {
width: 20px;
height: 20px;
border-radius: 4px;
border: 1px solid #0008;
}
.poss-apply-hex {
font-family: ui-monospace, monospace;
font-size: 0.78rem;
letter-spacing: 0.02em;
}
/* ── Help chip + tooltip ────────────────────────────────────── */
.help-chip {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border-radius: 50%;
background: #3a3a3a;
color: var(--text);
font-size: 0.82rem;
font-weight: 700;
cursor: help;
user-select: none;
letter-spacing: 0;
text-transform: none;
transition: background 0.15s;
/* Invisible hit-area expansion to ~32px without growing the visible chip */
box-shadow: 0 0 0 4px transparent;
}
.help-chip:hover { background: #5a5a5a; }
.help-chip:focus-visible {
background: #5a5a5a;
outline: none;
box-shadow: var(--focus-ring);
}
/* Mobile: pin tooltip to right edge to avoid overflow */
.help-tooltip {
position: absolute;
top: calc(100% + 6px);
left: auto;
right: 0;
z-index: 20;
width: min(340px, 90vw);
padding: 0.75rem 0.9rem;
background: #313131;
border: 1px solid #585858;
border-radius: 7px;
font-size: 0.82rem;
font-weight: 400;
color: #f2f2f2;
text-transform: none;
letter-spacing: normal;
line-height: 1.5;
opacity: 0;
transform: scale(0.7);
transform-origin: top right;
transition:
transform 220ms cubic-bezier(0.2, 0.85, 0.25, 1.25),
opacity 160ms ease-out;
pointer-events: none;
box-shadow: 0 12px 30px #000c;
}
/* On wider screens revert to left-aligned */
@media (min-width: 520px) {
.help-tooltip {
left: 0;
right: auto;
transform-origin: top left;
}
}
.help-tooltip b { color: #fff; }
.help-chip:hover ~ .help-tooltip,
.help-chip:focus ~ .help-tooltip,
.help-chip:focus-visible ~ .help-tooltip,
.help-tooltip:hover {
opacity: 1;
transform: scale(1);
pointer-events: auto;
}
/* ── History / guess rows ───────────────────────────────────── */
.history {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 1.25rem;
}
/* Entrance animation — neutralised by reduced-motion block below */
@keyframes guess-row-in {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: none; }
}
.row {
display: flex;
align-items: center;
gap: 0.6rem;
padding: 0.5rem;
background: var(--bg-elev-1);
border: 1px solid var(--border);
border-radius: var(--radius);
flex-wrap: wrap;
animation: guess-row-in 180ms ease-out;
}
.row .hex {
font-family: ui-monospace, monospace;
font-size: 0.9rem;
opacity: 0.8;
min-width: 5rem;
font-variant-numeric: tabular-nums;
}
/* ── Channel result blocks ──────────────────────────────────── */
.channels {
display: flex;
gap: 0.35rem;
margin-left: auto;
flex-wrap: wrap;
}
.channel {
display: flex;
flex-direction: column;
align-items: center;
padding: 0.3rem 0.55rem;
border-radius: 5px;
font-family: ui-monospace, monospace;
min-width: 4.4rem;
line-height: 1.15;
}
.channel-main {
font-size: 0.85rem;
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.channel-range {
font-size: 0.72rem;
font-weight: 500;
margin-top: 0.1rem;
font-variant-numeric: tabular-nums;
}
.channel.red { background: var(--c-red); color: #fff; }
.channel.yellow { background: var(--c-yellow); color: #1a1a1a; }
.channel.green { background: var(--c-green); color: #0a0a0a; }
.channel.exact { background: var(--c-exact); color: #0a0a0a; box-shadow: 0 0 0 2px #fff8; }
/* Colorblind icons — prepend glyph before channel value text */
.channel.red .channel-main::before { content: "\25CF "; opacity: 0.85; }
.channel.yellow .channel-main::before { content: "\25C6 "; opacity: 0.85; }
.channel.green .channel-main::before { content: "\25B2 "; opacity: 0.85; }
.channel.exact .channel-main::before { content: "\2605 "; opacity: 0.85; }
/* ── Nearness badge ─────────────────────────────────────────── */
.nearness {
display: inline-flex;
align-items: center;
justify-content: center;
margin-left: 0.35rem;
padding: 0.35rem 0.6rem;
border-radius: 5px;
font-family: ui-monospace, monospace;
font-size: 0.82rem;
font-weight: 700;
color: #fff;
min-width: 3.2rem;
text-align: center;
box-shadow: inset 0 0 0 1px #0004;
}
/* ── Legend chips ───────────────────────────────────────────── */
.chip {
display: inline-block;
padding: 0.15rem 0.45rem;
border-radius: 4px;
font-family: ui-monospace, monospace;
font-size: 0.78rem;
font-weight: 600;
margin: 0 0.15rem 0 0.6rem;
vertical-align: middle;
}
.chip.red { background: var(--c-red); color: #fff; }
.chip.yellow { background: var(--c-yellow); color: #1a1a1a; }
.chip.green { background: var(--c-green); color: #0a0a0a; }
.chip.exact { background: var(--c-exact); color: #0a0a0a; }
/* Colorblind icons for legend chips */
.chip.red::before { content: "\25CF "; }
.chip.yellow::before { content: "\25C6 "; }
.chip.green::before { content: "\25B2 "; }
.chip.exact::before { content: "\2605 "; }
/* ── Result panel ───────────────────────────────────────────── */
.result {
background: var(--bg-elev-1);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 1rem;
text-align: center;
margin-bottom: 1rem;
}
.result h2 { margin: 0 0 0.5rem; }
.result .reveal {
display: inline-block;
width: 32px;
height: 16px;
border-radius: 3px;
border: 1px solid #444;
vertical-align: middle;
margin: 0 0.3rem;
}
.best-line {
margin-top: 0.4rem;
opacity: 0.9;
font-size: 0.95rem;
}
.best-pct {
display: inline-block;
padding: 0.1rem 0.45rem;
border-radius: 4px;
font-family: ui-monospace, monospace;
font-weight: 700;
color: #fff;
font-size: 0.88rem;
box-shadow: inset 0 0 0 1px #0004;
}
/* Play-again button (injected by features agent) */
.play-again {
margin-top: 0.9rem;
font-size: 0.95rem;
font-weight: 600;
padding: 0.55rem 1.2rem;
background: var(--c-exact);
color: #0a0a0a;
border: 1px solid #1aae66;
border-radius: var(--radius-sm);
cursor: pointer;
}
.play-again:hover { background: #27dd88; }
.play-again:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
/* Stats line (under daily result) */
.stats-line {
display: flex;
justify-content: center;
gap: 1.25rem;
margin-top: 0.75rem;
font-size: 0.85rem;
color: var(--text-dim);
}
.stats-line .stat-value {
color: var(--text);
font-weight: 700;
font-variant-numeric: tabular-nums;
}
/* ── Share row ──────────────────────────────────────────────── */
.share-row {
display: flex;
align-items: center;
gap: 0.4rem;
justify-content: center;
flex-wrap: wrap;
margin-top: 0.9rem;
padding-top: 0.8rem;
border-top: 1px solid var(--border);
}
.share-label {
font-size: 0.78rem;
opacity: 0.65;
letter-spacing: 0.04em;
text-transform: uppercase;
margin-right: 0.25rem;
}
.share-btn {
font: inherit;
font-size: 0.85rem;
font-weight: 600;
color: #fff;
background: #3a3a3a;
border: 1px solid #4a4a4a;
padding: 0.55rem 1rem;
border-radius: var(--radius-sm);
cursor: pointer;
transition: transform 0.1s, background 0.15s;
}
.share-btn:hover { transform: translateY(-1px); }
.share-btn:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
.share-btn.x { background: #111; border-color: #333; }
.share-btn.x:hover { background: #000; }
.share-btn.fb { background: #1877f2; border-color: #1b6ed6; }
.share-btn.fb:hover { background: #1366d6; }
.share-btn.img { background: #2d7d46; border-color: #26683a; }
.share-btn.img:hover { background: #25713c; }
/* ── Daily badge (inline in .counter under the target) ──────── */
.daily-badge,
.daily-streak-wrap {
font-family: ui-monospace, monospace;
font-size: inherit;
color: var(--text-dim);
}
.daily-badge .sep,
.daily-streak-wrap .sep {
margin: 0 0.35rem;
opacity: 0.5;
}
.daily-streak {
color: var(--c-exact);
font-weight: 700;
font-variant-numeric: tabular-nums;
}
/* ── Mode switch (pill segmented control) ───────────────────── */
.mode-switch {
display: inline-flex;
background: var(--bg-elev-1);
border: 1px solid var(--border);
border-radius: 999px;
padding: 2px;
font-size: 0.8rem;
}
.mode-switch button {
background: transparent;
border: none;
padding: 0.5rem 1rem;
border-radius: 999px;
color: var(--text-dim);
cursor: pointer;
}
.mode-switch button:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
.mode-switch button.active {
background: var(--bg-elev-2);
color: var(--text);
}
/* ── Footer ─────────────────────────────────────────────────── */
footer {
margin-top: 1.5rem;
font-size: 0.82rem;
opacity: 0.7;
}
.legend, .note { margin: 0.3rem 0; }
/* ── Responsive ─────────────────────────────────────────────── */
@media (max-width: 480px) {
h1 { font-size: 1.4rem; }
.swatch { height: 140px; }
.channels { margin-left: 0; }
}
/* ── Reduced motion ─────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
transition: none !important;
animation: none !important;
}
}