This commit is contained in:
248
frontend/app/assets/css/_calculator.scss
Normal file
248
frontend/app/assets/css/_calculator.scss
Normal 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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user