194 lines
6.2 KiB
CSS
194 lines
6.2 KiB
CSS
/* Tofu — the planning template. A ruled form on paper: a label column on the
|
|
left, a writing line on the right. No card, badge, shadow, gradient or motion.
|
|
.sheet / .sheet-body get no rule: <article> and <form> are already plain blocks. */
|
|
|
|
/* --- Sheet -------------------------------------------------------------- */
|
|
|
|
.sheet-header {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: baseline;
|
|
justify-content: space-between;
|
|
gap: var(--space-3) var(--space-4);
|
|
padding-bottom: var(--space-3);
|
|
border-bottom: var(--rule-width) solid var(--rule-strong);
|
|
}
|
|
|
|
.sheet-title {
|
|
font-family: var(--font-document);
|
|
font-size: var(--size-title);
|
|
font-weight: var(--weight-normal);
|
|
line-height: var(--line-tight);
|
|
color: var(--ink);
|
|
}
|
|
|
|
.sheet-footer {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: var(--space-3);
|
|
margin-top: var(--space-4);
|
|
padding-top: var(--space-4);
|
|
border-top: var(--rule-width) solid var(--rule-strong);
|
|
}
|
|
|
|
/* --- Week navigation ---------------------------------------------------- */
|
|
|
|
.week-nav { display: flex; gap: var(--space-2); }
|
|
|
|
/* Chrome and focus come from base.css; only the square footprint is here. */
|
|
.week-nav-button {
|
|
min-width: var(--control-height); /* 44px square touch target, every viewport */
|
|
padding: var(--space-2);
|
|
}
|
|
|
|
/* --- Day ---------------------------------------------------------------- */
|
|
|
|
.day {
|
|
padding-block: var(--space-4);
|
|
border-bottom: var(--rule-width) solid var(--rule);
|
|
}
|
|
|
|
.day:last-of-type { border-bottom: 0; }
|
|
|
|
.day-title {
|
|
margin-bottom: var(--space-2);
|
|
font-family: var(--font-label);
|
|
font-size: var(--size-day);
|
|
font-weight: var(--weight-strong);
|
|
letter-spacing: var(--tracking-label);
|
|
line-height: var(--line-tight);
|
|
text-transform: uppercase;
|
|
color: var(--ink);
|
|
}
|
|
|
|
/* --- Field -------------------------------------------------------------- */
|
|
/* The label is ALWAYS rendered, left of the line, small and quiet. Baseline
|
|
alignment sits the label's baseline on the input's writing line. */
|
|
|
|
.field {
|
|
display: grid;
|
|
grid-template-columns: var(--label-column) 1fr;
|
|
align-items: baseline;
|
|
column-gap: var(--space-3);
|
|
row-gap: var(--space-1);
|
|
margin-block: var(--space-3);
|
|
}
|
|
|
|
.field-label {
|
|
justify-self: start;
|
|
font-family: var(--font-label);
|
|
font-size: var(--size-label);
|
|
font-weight: var(--weight-normal);
|
|
letter-spacing: var(--tracking-label);
|
|
line-height: var(--line-tight);
|
|
text-transform: uppercase;
|
|
color: var(--ink-label); /* 6.9:1 on paper, 6.0:1 on the focus wash */
|
|
}
|
|
|
|
.field-control {
|
|
position: relative; /* anchor for .suggestions */
|
|
min-width: 0; /* let a long value shrink instead of widening the grid */
|
|
}
|
|
|
|
.field-input {
|
|
display: block;
|
|
width: 100%;
|
|
min-height: var(--control-height);
|
|
padding: var(--field-pad-block) var(--field-pad-inline);
|
|
font-family: var(--font-document);
|
|
font-size: var(--size-body);
|
|
line-height: var(--line-body);
|
|
color: var(--ink);
|
|
background-color: transparent;
|
|
border: 0;
|
|
border-bottom: var(--rule-width) solid var(--rule-strong); /* the writing line, 3.0:1 */
|
|
border-radius: var(--radius);
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
}
|
|
|
|
.field-input::placeholder { color: var(--ink-label); opacity: 1; }
|
|
|
|
/* Focus = TWO simultaneous signals: the wash AND a thicker, darker writing line.
|
|
The outline goes only because both replacements are visible; padding-bottom
|
|
gives back the pixel the border takes, so the text never moves. */
|
|
.field-input:focus-visible {
|
|
outline: none;
|
|
background-color: var(--focus-wash);
|
|
border-bottom-color: var(--rule-focus);
|
|
border-bottom-width: var(--rule-width-focus);
|
|
padding-bottom: calc(var(--field-pad-block) - var(--rule-grow));
|
|
}
|
|
|
|
/* Forced-colors mode paints over our palette, so ask for a real outline there. */
|
|
@media (forced-colors: active) {
|
|
.field-input:focus-visible,
|
|
.button:focus-visible,
|
|
.week-nav-button:focus-visible { outline: var(--rule-width-focus) solid; }
|
|
}
|
|
|
|
/* The ingredient line is quieter than the dish (9.4:1, still AAA).
|
|
Ambiguity: §4.9 gives no modifier class, so this leans on document order. */
|
|
.day .field + .field .field-input { color: var(--ink-soft); }
|
|
|
|
/* --- Suggestions -------------------------------------------------------- */
|
|
/* Not a floating card: a plain box continuing the field's rule, no shadow.
|
|
Absolute only so an open list does not push the rest of the week down. */
|
|
|
|
.suggestions {
|
|
position: absolute;
|
|
inset-inline: 0;
|
|
top: 100%;
|
|
z-index: var(--layer-suggestions);
|
|
max-height: var(--suggestions-max-height);
|
|
overflow-y: auto;
|
|
list-style: none;
|
|
background-color: var(--paper);
|
|
border: var(--rule-width) solid var(--rule-strong);
|
|
border-top: 0;
|
|
border-radius: var(--radius);
|
|
}
|
|
|
|
.suggestion {
|
|
display: flex;
|
|
align-items: center;
|
|
min-height: var(--control-height);
|
|
padding: var(--space-2) var(--field-pad-inline);
|
|
border-inline-start: var(--rule-width-focus) solid transparent;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.suggestion + .suggestion { border-top: var(--rule-width) solid var(--rule); }
|
|
|
|
/* The active option is marked by a bar AND a wash — shape, not colour alone. */
|
|
.suggestion:hover,
|
|
.suggestion[aria-selected="true"] { background-color: var(--focus-wash); }
|
|
.suggestion[aria-selected="true"] { border-inline-start-color: var(--rule-focus); }
|
|
|
|
/* --- Compact viewport --------------------------------------------------- */
|
|
/* A media query cannot read a custom property, so 48rem is stated here. One
|
|
column, label ABOVE the line — the natural shape of a form on a phone, not a
|
|
degraded desktop: same type sizes, same 44px targets. */
|
|
|
|
@media (width < 48rem) {
|
|
#root {
|
|
padding: var(--space-5) var(--space-4);
|
|
border-inline: 0;
|
|
}
|
|
|
|
/* More air between fields than between a label and its own line: the label
|
|
must read as belonging to the line under it, not floating between two. */
|
|
.field {
|
|
grid-template-columns: 1fr;
|
|
align-items: start;
|
|
margin-block: var(--space-4);
|
|
}
|
|
|
|
/* Stacked, the label must share the value's left edge, not the rule's. */
|
|
.field-label { padding-inline-start: var(--field-pad-inline); }
|
|
|
|
.sheet-header { flex-direction: column; align-items: stretch; }
|
|
.week-nav { justify-content: flex-start; }
|
|
}
|