Feature 15: Recipe Detail and Kitchen Reading Mode (#162)
CI / ci (push) Successful in 6m44s

Co-authored-by: orchestrator-agent <20+orchestrator-agent@code.cubecraftcreations.com>
This commit was merged in pull request #162.
This commit is contained in:
2026-10-09 14:01:14 -04:00
committed by approver_agent
parent 14f7141e44
commit 4b592fdbbb
39 changed files with 4549 additions and 105 deletions
+547
View File
@@ -0,0 +1,547 @@
/* Mock 1k on mobile: the photo and the sheet run full-bleed past the
shell's 20 px side padding. From tablet up the page is Kitchen reading
mode (mock 2a, below). The page is at least as tall as the shell's scroll
area (a stretched flex item, so its height resolves the percentage) and
the sheet, or the reading view, grows into any room left, so the action
bar rests at the bottom of a short recipe. */
.page {
display: flex;
flex-direction: column;
min-height: 100%;
font-family: var(--font-body);
color: var(--color-text);
}
/* Kitchen reading mode is no wider than the handoff's tablet frame. */
.tablet,
.desktop {
width: 100%;
max-width: 1180px;
margin: 0 auto;
}
.hero {
position: relative;
height: 250px;
}
.mobile .hero,
.mobile .skeletonHero {
margin: 0 -20px;
}
/* Without a dinner to show, the back button sits alone above the message. */
.backRow {
padding-top: calc(16px + env(safe-area-inset-top));
}
.backRow .back {
position: static;
}
.photo {
width: 100%;
height: 100%;
}
.back {
position: absolute;
top: calc(16px + env(safe-area-inset-top));
left: 20px;
}
/* The sheet overlaps the photo by 28 px. */
.sheet {
position: relative;
flex-grow: 1;
margin-top: -28px;
display: flex;
flex-direction: column;
gap: 10px;
padding: 22px 20px 0;
border-radius: 32px 32px 0 0;
background: var(--color-bg);
}
.mobile .sheet {
margin-left: -20px;
margin-right: -20px;
}
.tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.title {
margin: 0;
font-family: var(--font-heading);
font-size: 28px;
font-weight: 400;
line-height: 1.05;
}
.why {
margin: 0;
padding: 12px 14px;
border-radius: 18px;
background: var(--color-accent-2-100);
color: var(--color-accent-2-900);
font-size: 13px;
line-height: 1.45;
}
.section {
display: flex;
flex-direction: column;
margin-top: 6px;
}
.sectionTitle {
margin: 0 0 4px;
font-family: var(--font-heading);
font-size: 18px;
font-weight: 400;
line-height: 1.15;
}
.ingredients,
.steps {
margin: 0;
padding: 0;
list-style: none;
font-size: 14px;
line-height: 1.45;
}
.ingredient {
display: flex;
justify-content: space-between;
gap: 12px;
padding: 7px 0;
border-bottom: 1px solid var(--color-divider);
}
.ingredient:last-child {
border-bottom: 0;
}
.note,
.qty {
color: var(--color-neutral-700);
}
.qty {
flex: none;
text-align: right;
}
.step {
display: flex;
gap: 12px;
padding: 8px 0;
}
.number {
flex: none;
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border-radius: var(--radius-pill);
background: var(--color-accent-100);
color: var(--color-accent-800);
font-size: 12px;
font-weight: 700;
}
.source {
margin: 4px 0 0;
font-size: 12px;
font-weight: 700;
color: var(--color-neutral-700);
}
.source a {
color: inherit;
}
.missing {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
margin: 6px 0 0;
padding: 12px 14px;
border-radius: 18px;
background: var(--color-neutral-100);
color: var(--color-neutral-800);
font-size: 13px;
line-height: 1.45;
}
.missing p {
margin: 0;
}
/* A 44 px tall tap target for the link. */
.fix {
display: inline-flex;
align-items: center;
min-height: 44px;
color: var(--color-accent-700);
font-weight: 700;
}
/* At the bottom of the page, which fills the scroll area, and held at the
bottom of the screen over a long recipe. Sticky offsets count from inside
the scroll area's padding, which on mobile already keeps clear of the nav
bar. */
.bar {
position: sticky;
bottom: 0;
z-index: 1;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
margin-top: 24px;
padding: 10px 0;
background: var(--color-bg);
}
.add {
flex: 1;
min-height: 52px;
font-size: 16px;
}
/* What became of an add or a favorite: a full row above the buttons. */
.status {
order: -1;
flex-basis: 100%;
}
.added {
margin: 0;
font-family: var(--font-body);
font-size: 14px;
font-weight: 600;
color: var(--color-accent-2-800);
}
.filled {
color: var(--color-accent);
fill: currentColor;
}
.skeletonHero {
height: 250px;
border-radius: 0;
}
.skeletonTitle {
height: 32px;
width: 70%;
}
/* Mock 2a, Kitchen reading mode: the header over two columns, the
ingredients card (400 px) and the steps. Ingredient and step text scale
with the text size chosen: the handoff's sizes are medium. */
.reading {
flex-grow: 1;
display: flex;
flex-direction: column;
gap: 22px;
}
.small {
--reading-scale: 0.85;
}
.medium {
--reading-scale: 1;
}
.large {
--reading-scale: 1.25;
}
.readingHeader {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 18px;
}
.readingHeader .back {
position: static;
flex: none;
}
.heading {
flex: 1 1 240px;
min-width: 0;
display: flex;
flex-direction: column;
gap: 6px;
}
.kicker {
margin: 0;
font-size: 13px;
font-weight: 700;
color: var(--color-neutral-700);
}
.readingTitle {
margin: 0;
font-family: var(--font-heading);
font-size: 36px;
font-weight: 400;
line-height: 1.05;
}
.keepOn {
display: flex;
align-items: center;
gap: 10px;
font-size: 13px;
font-weight: 700;
}
.keepOnHint {
margin: 0;
font-size: 14px;
line-height: 1.4;
color: var(--color-neutral-700);
}
/* The A / A / A control: an outlined pill, the size chosen in grape, each
A drawn at the size it stands for. */
.readingHeader .textSize {
gap: 0;
padding: 0;
border: 1px solid var(--color-divider);
background: transparent;
overflow: hidden;
}
.readingHeader .textSize > * {
padding: 0 14px;
border-radius: 0;
color: var(--color-text);
}
.readingHeader .textSize > [aria-checked="true"] {
background: var(--color-accent);
color: var(--on-accent);
box-shadow: none;
}
.readingHeader .textSize > :nth-child(1) {
font-size: 13px;
}
.readingHeader .textSize > :nth-child(2) {
font-size: 17px;
}
.readingHeader .textSize > :nth-child(3) {
font-size: 21px;
}
.columns {
flex-grow: 1;
display: grid;
grid-template-columns: 400px minmax(0, 1fr);
gap: 28px;
}
/* Below a 1024 px viewport the shell leaves too little beside a 400 px
card for the steps, so the columns share the width instead. */
@media (max-width: 1023px) {
.columns {
grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
}
}
.oneColumn {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.columns .ingredientsCard {
display: flex;
flex-direction: column;
gap: 6px;
padding: 22px;
border-radius: 30px;
}
.cardHead {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 12px;
margin-bottom: 6px;
}
.readingSectionTitle {
margin: 0;
font-family: var(--font-heading);
font-size: 22px;
font-weight: 400;
line-height: 1.15;
}
.setOutCount {
flex: none;
white-space: nowrap;
font-size: 13px;
font-weight: 700;
color: var(--color-neutral-700);
}
.checklist {
margin: 0;
padding: 0;
list-style: none;
}
.checkRow {
display: flex;
align-items: center;
gap: 14px;
padding: 4px 0;
border-bottom: 1px solid var(--color-divider);
}
.checkRow .check {
flex: 1;
min-width: 0;
gap: 14px;
}
.checkName {
font-size: calc(18px * var(--reading-scale));
font-weight: 400;
line-height: 1.3;
}
.setOut {
color: var(--color-neutral-600);
text-decoration: line-through;
}
.checkQty {
flex: none;
text-align: right;
font-size: calc(15px * var(--reading-scale));
color: var(--color-neutral-700);
}
.method {
flex-grow: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 14px;
}
.intro {
display: flex;
align-items: center;
gap: 16px;
}
.introPhoto {
flex: 1;
min-width: 0;
height: 96px;
}
.introWhy {
flex: 1.6;
padding: 14px 18px;
border-radius: 22px;
font-size: 14px;
}
.readingSteps {
display: flex;
flex-direction: column;
gap: 14px;
margin-top: 4px;
}
.stepList {
display: flex;
flex-direction: column;
gap: 14px;
margin: 0;
padding: 0;
list-style: none;
}
.readingStep {
display: flex;
align-items: flex-start;
gap: 16px;
padding: 6px 18px;
border-radius: 26px;
font-size: calc(18px * var(--reading-scale));
line-height: 1.45;
}
.stepCircle {
flex: none;
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: var(--radius-pill);
background: var(--color-surface);
font-family: var(--font-heading);
font-size: 18px;
}
.stepText {
padding-top: 6px;
}
.stepDone {
color: var(--color-neutral-600);
}
.stepCurrent {
padding: 18px;
background: var(--color-accent-100);
box-shadow: inset 0 0 0 2px var(--color-accent-300);
font-size: calc(22px * var(--reading-scale));
}
.stepCurrent .stepCircle {
background: var(--color-accent);
color: var(--on-accent);
}
/* The action bar rests at the bottom of the steps column, with Previous
and Next step at its right. */
.reading .bar {
margin-top: auto;
}
.reading .add {
flex: none;
}
.stepButtons {
display: flex;
gap: 10px;
margin-left: auto;
}