Calc2
All checks were successful
Build and Deploy Nuxt / build (push) Successful in 29s

This commit is contained in:
2026-10-02 02:14:29 +02:00
parent 325394150d
commit e36fef7290
21 changed files with 1745 additions and 46 deletions

View File

@@ -0,0 +1,248 @@
@use 'mixins' as *;
// ---------- Graphing calculator (/calculator) ----------
// desktop: the plane owns the space beside the sidebar the way the link
// map does — a full surface with the function dock and the zoom buttons
// floating over it. (The page transition only fades, so no transform
// steals the fixed positioning.)
.calc-stage {
position: fixed;
inset: 0 0 0 var(--sidebar-w);
transition: left 0.22s ease;
z-index: 1;
background: var(--bg);
}
// the plane fills the stage
.plot-wrap {
position: absolute;
inset: 0;
overflow: hidden;
background: var(--surface);
}
.plot-canvas {
display: block;
width: 100%;
height: 100%;
touch-action: none; // the plane owns drags and pinches, not the page
}
// the function dock, pinned to the plane's left edge
.calc-dock {
position: absolute;
top: 0;
left: 0;
bottom: 0;
z-index: 2;
display: flex;
flex-direction: column;
width: clamp(260px, 24vw, 320px);
padding: 18px 16px;
border-right: 1px solid var(--border);
background: color-mix(in srgb, var(--bg-elev) 92%, transparent);
backdrop-filter: blur(12px);
box-shadow: var(--shadow-lg);
overflow-y: auto;
}
.calc-dock-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin: 0 0 14px;
}
.calc-dock-title {
@include eyebrow(0.74rem, 750, 0.09em);
margin: 0;
}
.calc-rows {
display: flex;
flex-direction: column;
gap: 8px;
margin: 0;
padding: 0;
list-style: none;
}
.calc-row {
display: flex;
align-items: center;
gap: 8px;
}
// the curve's hue; a press hands it on to the next one in the palette
.calc-swatch {
flex: none;
width: 14px;
height: 14px;
border-radius: 50%;
border: 2px solid color-mix(in srgb, var(--text) 12%, transparent);
transition: transform 0.15s;
&:hover { transform: scale(1.2); }
}
.calc-input-box {
flex: 1;
display: flex;
align-items: center;
min-width: 0;
border: 1px solid var(--border);
border-radius: 10px;
background: var(--surface);
transition: border-color 0.15s, box-shadow 0.15s;
&:focus-within {
border-color: var(--accent);
box-shadow: var(--ring);
}
.calc-item.invalid & {
border-color: color-mix(in srgb, var(--danger) 55%, transparent);
}
}
// the "y =" the field answers to, set like the math it introduces
.calc-y {
flex: none;
padding: 0 2px 0 10px;
color: var(--faint);
font: italic 0.95rem/1 'Computer Modern', Georgia, serif;
}
.calc-input {
flex: 1;
min-width: 0;
padding: 9px 10px 9px 6px;
border: 0;
outline: none;
background: transparent;
color: var(--text);
font: 0.95rem/1.2 'Computer Modern', Georgia, serif;
&::placeholder { color: var(--faint); opacity: 0.75; }
&.input-error { color: var(--danger); }
}
.calc-remove:hover {
color: var(--danger);
background: color-mix(in srgb, var(--danger) 10%, transparent);
}
.calc-error {
display: block;
margin: 5px 2px 1px;
color: var(--danger);
font-size: 0.78rem;
}
.calc-add { margin-top: 12px; }
// the fold: the dock slides out to the left, under the app sidebar, and a
// quiet card takes its place at the plane's top-left corner
.calc-dock-enter-active,
.calc-dock-leave-active { transition: transform 0.22s ease; }
.calc-dock-enter-from,
.calc-dock-leave-to { transform: translateX(-105%); }
.calc-dock-open {
position: absolute;
top: 10px;
left: 12px;
z-index: 2;
display: flex;
align-items: center;
gap: 6px;
padding: 7px 12px;
border: 1px solid var(--border);
border-radius: 11px;
background: color-mix(in srgb, var(--bg-elev) 84%, transparent);
backdrop-filter: blur(10px);
box-shadow: var(--shadow-sm);
color: var(--muted);
font-size: 0.84rem;
font-weight: 650;
white-space: nowrap;
transition: color 0.15s, background-color 0.15s;
&:hover { color: var(--text); background: var(--surface-2); }
}
// the button row floating on the plane's top-right corner — clear of the dock
.plot-toolbar {
position: absolute;
top: 10px;
right: 12px;
z-index: 2;
display: flex;
gap: 2px;
padding: 4px;
border: 1px solid var(--border);
border-radius: 11px;
background: color-mix(in srgb, var(--bg-elev) 84%, transparent);
backdrop-filter: blur(10px);
box-shadow: var(--shadow-sm);
}
// where the cursor stands, bottom-right so the dock never covers it
.plot-readout {
position: absolute;
bottom: 10px;
right: 12px;
padding: 4px 10px;
border: 1px solid var(--border);
border-radius: 8px;
background: color-mix(in srgb, var(--bg-elev) 84%, transparent);
backdrop-filter: blur(10px);
color: var(--muted);
font: 0.78rem/1.3 'Computer Modern', Georgia, serif;
font-variant-numeric: tabular-nums;
pointer-events: none;
}
// the top bar is in the flow here, so the calculator folds into panels of
// the page: the dock first, then the plane as its own card
@media (max-width: 900px) {
.calc-stage {
position: static;
inset: auto;
display: flex;
flex-direction: column;
background: transparent;
}
.calc-dock {
position: static;
order: 1;
width: auto;
margin-bottom: 16px;
border: 1px solid var(--border);
border-radius: 14px;
background: var(--bg-elev);
backdrop-filter: none;
box-shadow: var(--shadow-sm);
overflow: visible;
}
.plot-wrap {
position: static;
order: 2;
width: 100%;
height: min(66dvh, 560px);
border: 1px solid var(--border);
border-radius: 14px;
}
.plot-toolbar { top: 8px; right: 8px; }
// during the fold the dock lifts out of the flow, so the plane takes the
// width back at once rather than after the slide
.calc-dock-leave-active { position: absolute; left: 0; right: 0; }
}

View File

@@ -1,3 +1,5 @@
@use 'mixins' as *;
// ---------- Graph view (the link map) ----------
// desktop: the map owns the space beside the sidebar the way the open
@@ -21,11 +23,16 @@
touch-action: none; // the map owns drags and pinches, not the page
}
.graph-toolbar {
// top-right corner of the map: the button row, with the display menu
// folding out under it
.graph-tools {
position: absolute;
top: 16px;
right: 18px;
z-index: 2;
}
.graph-toolbar {
display: flex;
gap: 2px;
padding: 4px;
@@ -34,6 +41,53 @@
background: color-mix(in srgb, var(--bg-elev) 84%, transparent);
backdrop-filter: blur(10px);
box-shadow: var(--shadow-sm);
// the display button while its menu is open, the ⋮ menu's way
.panel-icon-btn.open { color: var(--text); background: var(--surface-2); }
}
// the collapsible display menu, hinged under the buttons
.graph-menu {
@include dropdown-panel($z: 20, $overflow: visible);
right: 0;
min-width: 216px;
.graph-menu-row {
@include menu-row(8px 10px, 14px);
justify-content: space-between;
text-align: left;
&:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
}
}
// the switch a menu row wears: a pill with a knob that rides across
.graph-switch {
position: relative;
flex: none;
width: 32px;
height: 18px;
border-radius: 999px;
background: var(--border-strong);
transition: background-color 0.15s;
.knob {
position: absolute;
top: 2px;
left: 2px;
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--bg-elev);
box-shadow: var(--shadow-sm);
transition: transform 0.15s ease;
}
&.on {
background: var(--accent);
.knob { transform: translateX(14px); }
}
}
.graph-state {
@@ -62,7 +116,7 @@
box-shadow: var(--shadow-sm);
}
.graph-toolbar { top: 10px; right: 10px; }
.graph-tools { top: 10px; right: 10px; }
.graph-state { position: static; display: block; padding: 8px 0 32px; }
}

View File

@@ -69,6 +69,58 @@
p { margin: 0.2em 0; }
}
// math callout boxes — ```theorem, ```example, … (see utils/markdown.ts):
// the blockquote's shape, but the left edge wears the kind's colour, and
// the label row is a native <details> toggle — the box folds away under it.
.md-box {
@each $kind in example theorem corollary definition proof proposition lemma {
&.md-box--#{$kind} { --md-box: var(--box-#{$kind}); }
}
border-left-color: var(--md-box);
background: color-mix(in srgb, var(--md-box) 6%, transparent);
.md-box-title {
display: flex;
align-items: center;
gap: 0.6em;
margin: 0;
color: var(--md-box);
font-weight: 730;
font-size: 0.85em;
letter-spacing: 0.05em;
text-transform: uppercase;
cursor: pointer;
user-select: none;
list-style: none;
transition: margin-bottom 0.15s ease;
&::-webkit-details-marker { display: none; }
// the caret: points down while open, turns aside when the box folds
&::before {
content: '';
flex: none;
width: 0.42em;
height: 0.42em;
border-right: 2px solid currentColor;
border-bottom: 2px solid currentColor;
transform: rotate(45deg);
transition: transform 0.15s ease;
}
}
details[open] .md-box-title { margin-bottom: 0.4em; }
// the fold's intent lands on the <details> the moment it is clicked
// (see MarkdownView): the label closes in and the caret turns at the
// *start* of the fold, while [open] itself settles only as it ends
details[data-open='0'] .md-box-title { margin-bottom: 0; }
details:not([open]) .md-box-title::before,
details[data-open='0'] .md-box-title::before { transform: rotate(-45deg); }
}
hr { border: 0; border-top: 1px solid var(--border); margin: 2.4em 0; }
table {

View File

@@ -25,6 +25,16 @@
--shadow-lg: 0 4px 12px rgb(16 24 40 / 8%), 0 16px 40px -12px rgb(16 24 40 / 18%);
--ring: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
// math callout boxes — ```theorem, ```example, … (see utils/markdown.ts):
// each kind keeps one hue day and night, so the colour always means the same
--box-example: #16a34a;
--box-theorem: #d1344b;
--box-corollary: #d97706;
--box-definition: #2563eb;
--box-proof: #64748b;
--box-proposition: #7c3aed;
--box-lemma: #0d9488;
// code token colors (light)
--tok-key: #8250df;
--tok-str: #0a3069;
@@ -64,6 +74,15 @@
--shadow-sm: 0 1px 2px rgb(0 0 0 / 30%);
--shadow-lg: 0 4px 16px rgb(0 0 0 / 40%), 0 24px 48px -16px rgb(0 0 0 / 55%);
// math callout boxes (dark)
--box-example: #4ade80;
--box-theorem: #f47085;
--box-corollary: #fbbf24;
--box-definition: #60a5fa;
--box-proof: #94a3b8;
--box-proposition: #a78bfa;
--box-lemma: #2dd4bf;
// code token colors (dark)
--tok-key: #c792ea;
--tok-str: #9ece6a;

View File

@@ -26,4 +26,5 @@
@use 'editor'; // editor, cheatsheet, confirm, fullscreen
@use 'book'; // the reader: an open book of article pages
@use 'graph'; // the link map
@use 'calculator'; // the graphing calculator: function rows + the plane
@use 'transitions'; // route fades, scrollbars