jhxk68cdy: Add the five approved button styles, the glossy icon button and a dev story page

This commit is contained in:
2026-10-19 21:55:26 -06:00
parent 3306a24d33
commit dd47cfd911
7 changed files with 1010 additions and 3 deletions
+492
View File
@@ -0,0 +1,492 @@
/*
* Button system (spec "Buttons", ADR 0001 sections 6 to 8).
* Five styles plus the glossy crimson icon button, ported from the mockup's
* recolored rules. Tokens only; white and black appear only on crimson faces.
* Every class sets margin, padding, font and color itself so the legacy
* `button` rules and any `button { color: inherit }` never leak in.
*/
/* Shared base for every style. */
.btn-flip,
.btn-grow,
.btn-sweep,
.btn-depth,
.btn-spark,
.btn-gloss {
appearance: none;
margin: 0;
border: 0;
font-family: inherit;
cursor: pointer;
position: relative;
overflow: hidden;
isolation: isolate;
opacity: 1;
-webkit-tap-highlight-color: transparent;
}
.btn-flip svg,
.btn-grow svg,
.btn-depth svg,
.btn-spark svg,
.btn-gloss svg { flex: none; }
/* 1. Flip reveal: Post. Ready, disabled ("write first") and busy ("posting"). */
.btn-flip {
height: 42px;
min-width: 118px;
padding: 0 22px;
border-radius: var(--r-ctl);
background: var(--ink);
color: var(--bg);
font-size: 13px;
font-weight: 800;
letter-spacing: .14em;
text-transform: uppercase;
box-shadow: var(--shadow-btn);
transition: transform .3s cubic-bezier(.23,1,.32,1);
}
.btn-flip .btn-flip-front {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
height: 100%;
color: var(--bg);
transition: transform .6s cubic-bezier(.23,1,.32,1);
}
.btn-flip .btn-flip-back {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
color: var(--on-accent);
background: linear-gradient(135deg, var(--accent-deep), var(--accent));
transform: translateY(100%);
transition: transform .6s cubic-bezier(.23,1,.32,1);
}
.btn-flip svg { width: 16px; height: 16px; }
.btn-flip:hover .btn-flip-front,
.btn-flip:focus-visible .btn-flip-front { transform: translateY(-200%); }
.btn-flip:hover .btn-flip-back,
.btn-flip:focus-visible .btn-flip-back { transform: none; }
.btn-flip:active:not(:disabled) { transform: scale(.95); }
/* Disabled: solid graphite face, readable text, never faded. */
.btn-flip:disabled {
opacity: 1;
cursor: not-allowed;
background: var(--tile-3);
color: var(--ink-2);
box-shadow: inset 0 0 0 1px var(--line-2);
}
.btn-flip:disabled .btn-flip-front { color: var(--ink-2); }
.btn-flip:disabled .btn-flip-back {
background: var(--tile-3);
color: var(--ink);
font-size: 12px;
}
/* Busy (posting): keeps the ink face, shows "Posting", never flips. */
.btn-flip.btn-flip-busy:disabled {
cursor: progress;
background: var(--ink);
color: var(--bg);
box-shadow: var(--shadow-btn);
}
.btn-flip.btn-flip-busy:disabled .btn-flip-front { color: var(--bg); transform: none; }
.btn-flip.btn-flip-busy .btn-flip-back { transform: translateY(100%); }
/* 2. Circle expand: the new-posts pill. */
.btn-grow {
display: block;
width: 178px;
height: 42px;
padding: 0;
border-radius: 999px;
background: var(--glass);
color: var(--ink);
-webkit-backdrop-filter: blur(16px);
backdrop-filter: blur(16px);
box-shadow: inset 0 0 0 1px var(--line-2), var(--shadow-float);
}
.btn-grow .btn-grow-circle {
position: absolute;
left: 4px;
top: 4px;
bottom: 4px;
width: 34px;
border-radius: 999px;
background: linear-gradient(180deg, var(--accent-hi), var(--accent) 55%, var(--accent-lo));
transition: width .45s cubic-bezier(.65,0,.076,1);
}
.btn-grow .btn-grow-circle svg {
position: absolute;
left: 9px;
top: 9px;
width: 16px;
height: 16px;
color: var(--on-accent);
transition: transform .45s cubic-bezier(.65,0,.076,1);
}
.btn-grow .btn-grow-text {
position: relative;
z-index: 1;
display: block;
padding-left: 40px;
line-height: 42px;
font-size: 11.5px;
font-weight: 800;
letter-spacing: .16em;
text-transform: uppercase;
white-space: nowrap;
color: var(--ink);
transition: color .45s cubic-bezier(.65,0,.076,1);
}
.btn-grow:hover .btn-grow-circle,
.btn-grow:focus-visible .btn-grow-circle { width: calc(100% - 8px); }
.btn-grow:hover .btn-grow-circle svg,
.btn-grow:focus-visible .btn-grow-circle svg { transform: translateY(-3px); }
.btn-grow:hover .btn-grow-text,
.btn-grow:focus-visible .btn-grow-text { color: var(--on-accent); }
.btn-grow:active { transform: scale(.97); }
/* 3. Outline fill sweep: category chips and Cancel. Selected stays filled. */
.btn-sweep {
display: inline-flex;
align-items: center;
justify-content: center;
height: 34px;
padding: 0 15px;
border-radius: 999px;
background: transparent;
color: var(--ink);
font-size: 12.5px;
font-weight: 600;
white-space: nowrap;
box-shadow: inset 0 0 0 1.5px var(--line-2);
transition: color .3s, box-shadow .3s, transform .15s;
}
.btn-sweep::before {
content: "";
position: absolute;
z-index: -1;
width: 170px;
height: 170px;
border-radius: 50%;
background: var(--ink);
top: 100%;
left: 100%;
transition: top .35s var(--ease), left .35s var(--ease);
}
.btn-sweep:hover,
.btn-sweep:focus-visible,
.btn-sweep.btn-sweep-on {
color: var(--bg);
box-shadow: inset 0 0 0 1.5px var(--ink);
}
.btn-sweep:hover::before,
.btn-sweep:focus-visible::before,
.btn-sweep.btn-sweep-on::before { top: -70px; left: -40px; }
.btn-sweep:active { transform: scale(.95); }
/* 4. Soft depth, rising liquid: mobile More sheet rows and the theme switch. */
.btn-depth {
display: flex;
align-items: center;
gap: 14px;
width: 100%;
padding: 14px;
border-radius: var(--r-card);
background: var(--bg);
color: var(--ink);
text-align: left;
font-size: 14px;
box-shadow: 6px 6px 14px var(--neu-d), -6px -6px 14px var(--neu-l);
transition: color .3s, box-shadow .25s;
}
.btn-depth::before,
.btn-depth::after {
content: "";
position: absolute;
width: 140%;
height: 240%;
border-radius: 50%;
z-index: -1;
transition: all .5s .05s cubic-bezier(.55,0,.1,1);
}
.btn-depth::before {
left: 50%;
top: 100%;
transform: translateX(-50%) scaleX(1.25);
background: color-mix(in srgb, var(--accent), transparent 82%);
}
.btn-depth::after {
left: 55%;
top: 180%;
transform: translateX(-50%) scaleX(1.45);
background: var(--accent);
}
.btn-depth .btn-depth-icon {
width: 44px;
height: 44px;
flex: none;
display: grid;
place-items: center;
border-radius: var(--r-ctl);
background: var(--tile-2);
color: var(--ink);
transition: background-color .3s, color .3s;
}
.btn-depth .btn-depth-icon svg { width: 20px; height: 20px; }
.btn-depth .btn-depth-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.btn-depth .btn-depth-label { font-size: 15px; font-weight: 700; color: inherit; }
.btn-depth .btn-depth-sub { font-size: 12.5px; color: var(--ink-3); transition: color .3s; }
.btn-depth:hover,
.btn-depth:focus-visible { color: var(--on-accent); }
.btn-depth:hover::before,
.btn-depth:focus-visible::before {
top: -35%;
background: var(--accent);
transform: translateX(-50%) scaleY(1.3) scaleX(.8);
}
.btn-depth:hover::after,
.btn-depth:focus-visible::after { top: -45%; transform: translateX(-50%) scaleY(1.3) scaleX(.8); }
.btn-depth:hover .btn-depth-sub,
.btn-depth:focus-visible .btn-depth-sub { color: color-mix(in srgb, white 82%, transparent); }
.btn-depth:hover .btn-depth-icon,
.btn-depth:focus-visible .btn-depth-icon { background: color-mix(in srgb, white 16%, transparent); color: var(--on-accent); }
.btn-depth:active { box-shadow: inset 4px 4px 12px var(--neu-d), inset -4px -4px 12px var(--neu-l); }
/* 5. Sparkle glow: Sign in (fold + hexagon) and the mobile new-post button (plus). */
.btn-spark {
--spark-round: 14px;
display: flex;
align-items: center;
justify-content: center;
min-width: 180px;
height: 50px;
padding: 0 22px;
border-radius: var(--spark-round);
color: var(--on-accent);
background:
radial-gradient(65% 65% at 50% 100%, var(--accent-glow) 0%, var(--accent-glow-0) 100%),
linear-gradient(0deg, var(--accent), var(--accent));
box-shadow: 0 18px 36px -14px color-mix(in srgb, var(--accent), transparent 15%);
transition: transform .25s;
}
.btn-spark::before,
.btn-spark::after {
content: "";
position: absolute;
inset: var(--spark-inset);
border-radius: calc(var(--spark-round) - var(--spark-inset));
z-index: 0;
transition: all .5s ease-in-out;
}
.btn-spark::before {
--spark-inset: 1px;
background: linear-gradient(178deg, color-mix(in srgb, white 24%, transparent) 0%, transparent 100%);
}
.btn-spark::after {
--spark-inset: 2px;
background:
radial-gradient(65% 65% at 50% 100%, var(--accent-glow) 0%, var(--accent-glow-0) 100%),
linear-gradient(0deg, var(--accent), var(--accent));
}
.btn-spark:active { transform: scale(.96); }
.btn-spark .btn-spark-fold {
z-index: 1;
position: absolute;
top: 0;
right: 0;
width: 1rem;
height: 1rem;
transition: all .5s ease-in-out;
background: radial-gradient(100% 75% at 55%, var(--accent-glow) 0%, var(--accent-glow-0) 100%);
box-shadow: 0 0 3px black;
border-bottom-left-radius: .5rem;
border-top-right-radius: var(--spark-round);
}
.btn-spark .btn-spark-fold::after {
content: "";
position: absolute;
top: 0;
right: 0;
width: 150%;
height: 150%;
transform: rotate(45deg) translateY(-18px);
background: var(--bg);
pointer-events: none;
}
.btn-spark:hover .btn-spark-fold,
.btn-spark:focus-visible .btn-spark-fold { margin-top: -1rem; margin-right: -1rem; }
.btn-spark .btn-spark-pts { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 1; }
.btn-spark .btn-spark-pts i {
position: absolute;
bottom: -10px;
width: 2px;
height: 2px;
border-radius: 50%;
background: var(--on-accent);
animation: btn-spark-rise infinite ease-in-out;
}
@keyframes btn-spark-rise {
0% { transform: translateY(0); }
85% { opacity: 0; }
100% { transform: translateY(-60px); opacity: 0; }
}
.btn-spark .btn-spark-pts i:nth-child(1) { left: 10%; animation-duration: 2.35s; animation-delay: .2s; }
.btn-spark .btn-spark-pts i:nth-child(2) { left: 30%; opacity: .7; animation-duration: 2.5s; animation-delay: .5s; }
.btn-spark .btn-spark-pts i:nth-child(3) { left: 25%; opacity: .8; animation-duration: 2.2s; animation-delay: .1s; }
.btn-spark .btn-spark-pts i:nth-child(4) { left: 44%; opacity: .6; animation-duration: 2.05s; }
.btn-spark .btn-spark-pts i:nth-child(5) { left: 50%; animation-duration: 1.9s; }
.btn-spark .btn-spark-pts i:nth-child(6) { left: 75%; opacity: .5; animation-duration: 1.5s; animation-delay: 1.5s; }
.btn-spark .btn-spark-pts i:nth-child(7) { left: 88%; opacity: .9; animation-duration: 2.2s; animation-delay: .2s; }
.btn-spark .btn-spark-pts i:nth-child(8) { left: 58%; opacity: .8; animation-duration: 2.25s; animation-delay: .2s; }
.btn-spark .btn-spark-pts i:nth-child(9) { left: 96%; opacity: .6; animation-duration: 2.6s; animation-delay: .1s; }
.btn-spark .btn-spark-pts i:nth-child(10) { left: 65%; animation-duration: 2.5s; animation-delay: .2s; }
.btn-spark .btn-spark-inner {
position: relative;
z-index: 2;
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--on-accent);
font-weight: 700;
font-size: 15px;
letter-spacing: .06em;
}
.btn-spark .btn-spark-hex {
width: 18px;
height: 18px;
fill: transparent;
stroke: var(--on-accent);
stroke-width: 2.2;
stroke-linejoin: round;
}
.btn-spark:hover .btn-spark-hex,
.btn-spark:focus-visible .btn-spark-hex {
animation: btn-spark-dash 1s linear forwards, btn-spark-fill .1s linear forwards .95s;
}
@keyframes btn-spark-dash { from { stroke-dasharray: 0 0 0 0; } to { stroke-dasharray: 68 68 0 0; } }
@keyframes btn-spark-fill { to { fill: var(--on-accent); } }
/* Mobile new-post variant: square, no fold, the plus turns 90 degrees. */
.btn-spark.btn-spark-fab {
--spark-round: 16px;
min-width: 0;
width: 56px;
height: 56px;
padding: 0;
box-shadow: 0 12px 26px -10px color-mix(in srgb, var(--accent), transparent 15%);
}
.btn-spark.btn-spark-fab::after { display: none; }
.btn-spark.btn-spark-fab:active { transform: scale(.94); }
.btn-spark .btn-spark-plus {
position: relative;
z-index: 2;
width: 24px;
height: 24px;
color: var(--on-accent);
transition: transform .5s cubic-bezier(.2,.9,.3,1.4);
}
.btn-spark:hover .btn-spark-plus,
.btn-spark:focus-visible .btn-spark-plus { transform: rotate(90deg); }
/* Glossy crimson icon button: send comment, delete confirm, play. */
.btn-gloss {
display: inline-grid;
place-items: center;
width: 44px;
height: 44px;
padding: 0;
border-radius: var(--r-ctl);
color: var(--on-accent);
background: linear-gradient(180deg, var(--accent-hi), var(--accent) 50%, var(--accent-lo));
box-shadow:
inset 0 1px 0 color-mix(in srgb, white 32%, transparent),
inset 0 -1px 0 color-mix(in srgb, black 28%, transparent),
0 0 0 1px var(--accent-lo),
0 12px 26px -14px var(--accent);
transition: transform .18s var(--ease), box-shadow .3s;
}
.btn-gloss.btn-gloss-round { border-radius: 50%; }
.btn-gloss.btn-gloss-sm { width: 36px; height: 36px; }
.btn-gloss svg { width: 18px; height: 18px; color: var(--on-accent); }
.btn-gloss::before {
content: "";
position: absolute;
left: 0;
right: 0;
top: 0;
height: 50%;
background: linear-gradient(180deg, color-mix(in srgb, white 20%, transparent), transparent);
z-index: -1;
}
.btn-gloss::after {
content: "";
position: absolute;
top: -10%;
bottom: -10%;
width: 55%;
left: -90%;
background: linear-gradient(100deg, transparent, color-mix(in srgb, white 42%, transparent), transparent);
transform: skewX(-20deg);
transition: left .8s var(--ease);
}
.btn-gloss:hover::after,
.btn-gloss:focus-visible::after { left: 140%; }
.btn-gloss:active:not(:disabled) { transform: scale(.96); }
/* Disabled keeps a solid face (no opacity) so the icon stays readable. */
.btn-gloss:disabled {
opacity: 1;
cursor: not-allowed;
background: var(--tile-3);
color: var(--ink-2);
box-shadow: inset 0 0 0 1px var(--line-2);
}
.btn-gloss:disabled svg { color: var(--ink-2); }
.btn-gloss:disabled::before,
.btn-gloss:disabled::after { display: none; }
/* Reduced motion: hover and press states still change color, but nothing moves. */
@media (prefers-reduced-motion: reduce) {
.btn-flip,
.btn-flip *,
.btn-grow,
.btn-grow *,
.btn-sweep,
.btn-sweep::before,
.btn-depth,
.btn-depth *,
.btn-depth::before,
.btn-depth::after,
.btn-spark,
.btn-spark *,
.btn-spark::before,
.btn-spark::after,
.btn-gloss,
.btn-gloss::before,
.btn-gloss::after {
transition: none !important;
animation: none !important;
}
.btn-flip:active,
.btn-grow:active,
.btn-sweep:active,
.btn-spark:active,
.btn-gloss:active { transform: none !important; }
.btn-grow:hover .btn-grow-circle svg,
.btn-grow:focus-visible .btn-grow-circle svg,
.btn-spark:hover .btn-spark-plus,
.btn-spark:focus-visible .btn-spark-plus { transform: none !important; }
.btn-spark .btn-spark-pts { display: none; }
.btn-spark:hover .btn-spark-hex,
.btn-spark:focus-visible .btn-spark-hex { fill: var(--on-accent); }
.btn-gloss::after { display: none; }
}