Add catalog subject banner feature: Introduce optional header images for subjects in the catalog, update CONTRIBUTING.md with guidelines for adding banners, and modify catalog data structure to support banner URLs. Enhance frontend components and styles to display banners effectively. Update TypeScript types and curriculum structure to accommodate new banner functionality.

This commit is contained in:
Hananoshika Yomaru 2026-05-20 12:40:51 -07:00
parent 8a7f6449a7
commit 7636bb4a86
35 changed files with 4140 additions and 10 deletions

View file

@ -0,0 +1,371 @@
// Break out of prose max-width (68ch) to the same width as .container
.book-prose .math-widget-mount:has(.va-workbench) {
max-width: none;
}
.va-workbench {
--va-text: var(--ink);
--va-operator: var(--muted);
--va-carry: #dc2626;
--va-borrow: var(--accent);
--va-result: var(--success);
--va-line: var(--line);
--va-highlight: color-mix(in srgb, var(--accent) 14%, transparent);
--va-highlight-border: color-mix(in srgb, var(--accent) 38%, transparent);
--va-error: #dc2626;
--va-canvas-dot: color-mix(in srgb, var(--line) 90%, transparent);
--va-panel-bg: var(--card-bg);
--va-panel-border: var(--card-border);
--va-stage-bg: var(--paper);
width: min(1100px, 92vw);
max-width: none;
margin-top: 1.25rem;
}
[data-theme="dark"] .va-workbench,
:root:not([data-theme="light"]) .va-workbench {
--va-carry: #f87171;
--va-error: #f87171;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) .va-workbench {
--va-carry: #f87171;
--va-error: #f87171;
}
}
.va-workbench__shell {
display: flex;
flex-direction: column;
min-height: 28rem;
border: 1px solid var(--va-panel-border);
border-radius: 14px;
overflow: hidden;
background: var(--va-panel-bg);
box-shadow: 0 10px 32px var(--shadow-soft);
}
@media (min-width: 768px) {
.va-workbench__shell {
flex-direction: row;
min-height: 32rem;
}
}
.va-workbench__sidebar {
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1.25rem 1.5rem;
border-bottom: 1px solid var(--line);
background: var(--va-panel-bg);
overflow-y: auto;
}
@media (min-width: 768px) {
.va-workbench__sidebar {
width: 18rem;
border-bottom: none;
border-right: 1px solid var(--line);
}
}
.va-workbench__brand {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 0.25rem;
}
.va-workbench__brand-icon {
display: flex;
align-items: center;
justify-content: center;
width: 2.5rem;
height: 2.5rem;
border-radius: 0.75rem;
background: var(--accent);
color: var(--paper);
font-size: 1.25rem;
font-weight: 700;
box-shadow: 0 4px 12px var(--shadow-soft);
}
.va-workbench__brand-text {
font-size: 1rem;
line-height: 1.3;
color: var(--ink);
}
.va-workbench__brand-text strong {
color: var(--accent);
font-weight: 600;
}
.va-workbench__field {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.va-workbench__label {
font-size: 0.8125rem;
font-weight: 600;
color: var(--soft-ink);
}
.va-workbench__op-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.5rem;
}
.va-workbench__op-btn {
font: inherit;
font-size: 1.25rem;
font-weight: 700;
padding: 0.5rem;
border: 1px solid var(--line);
border-radius: 0.5rem;
background: color-mix(in srgb, var(--paper) 60%, var(--card-bg));
color: var(--soft-ink);
cursor: pointer;
transition:
background 0.12s ease,
border-color 0.12s ease,
color 0.12s ease;
}
.va-workbench__op-btn:hover {
border-color: var(--accent);
color: var(--ink);
}
.va-workbench__op-btn--active {
border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
background: color-mix(in srgb, var(--accent) 12%, var(--card-bg));
color: var(--accent);
}
.va-workbench__textarea {
width: 100%;
padding: 0.75rem 1rem;
font-family: var(--mono);
font-size: 1.0625rem;
line-height: 1.4;
color: var(--ink);
background: color-mix(in srgb, var(--paper) 50%, var(--card-bg));
border: 1px solid var(--line);
border-radius: 0.5rem;
resize: vertical;
min-height: 5rem;
}
.va-workbench__textarea:focus {
outline: 2px solid var(--accent);
outline-offset: 1px;
}
.va-workbench__hint,
.va-workbench__footer-note {
font-size: 0.75rem;
line-height: 1.45;
color: var(--muted);
margin: 0;
}
.va-workbench__generate {
font: inherit;
font-weight: 600;
width: 100%;
padding: 0.75rem 1rem;
border: none;
border-radius: 0.5rem;
background: var(--accent);
color: var(--paper);
cursor: pointer;
box-shadow: 0 4px 14px var(--shadow-soft);
transition:
filter 0.12s ease,
transform 0.08s ease;
}
.va-workbench__generate:hover {
filter: brightness(1.06);
}
.va-workbench__generate:active {
transform: scale(0.98);
}
.va-workbench__anim {
padding-top: 1rem;
border-top: 1px solid var(--line);
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.va-workbench__transport {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
padding: 0.35rem;
border: 1px solid var(--line);
border-radius: 0.5rem;
background: color-mix(in srgb, var(--paper) 40%, var(--card-bg));
}
.va-workbench__transport-btn {
font: inherit;
font-size: 1.5rem;
line-height: 1;
padding: 0.35rem 0.65rem;
border: none;
border-radius: 0.35rem;
background: transparent;
color: var(--soft-ink);
cursor: pointer;
}
.va-workbench__transport-btn:hover {
color: var(--accent);
background: var(--card-bg);
}
.va-workbench__play {
font: inherit;
font-weight: 600;
flex: 1;
padding: 0.45rem 1rem;
border: none;
border-radius: 0.35rem;
background: var(--accent);
color: var(--paper);
cursor: pointer;
}
.va-workbench__play:hover {
filter: brightness(1.06);
}
.va-workbench__speed {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.75rem;
color: var(--muted);
}
.va-workbench__speed-slider {
flex: 1;
accent-color: var(--accent);
}
.va-workbench__footer-note {
margin-top: auto;
text-align: center;
}
.va-workbench__stage {
flex: 1;
display: flex;
flex-direction: column;
min-height: 16rem;
background: var(--va-stage-bg);
position: relative;
}
.va-workbench__message-box {
position: absolute;
top: 1rem;
left: 50%;
transform: translateX(-50%);
z-index: 2;
width: min(92%, 32rem);
padding: 0.85rem 1.1rem;
text-align: center;
border: 1px solid var(--line);
border-radius: 0.75rem;
background: color-mix(in srgb, var(--card-bg) 92%, transparent);
backdrop-filter: blur(8px);
box-shadow: 0 8px 24px var(--shadow-soft);
}
.va-workbench__message-box--error .va-workbench__message {
color: var(--va-error);
}
.va-workbench__message {
font-size: 1rem;
font-weight: 600;
color: var(--ink);
margin: 0 0 0.5rem;
line-height: 1.4;
}
.va-workbench__progress-track {
width: 100%;
height: 0.35rem;
border-radius: 999px;
background: var(--line);
overflow: hidden;
}
.va-workbench__progress-fill {
height: 100%;
background: var(--accent);
transition: width 0.25s ease;
}
.va-workbench__step-counter {
font-size: 0.75rem;
color: var(--muted);
margin: 0.35rem 0 0;
}
.va-workbench__canvas-wrap {
flex: 1;
position: relative;
min-height: 14rem;
background-color: var(--va-stage-bg);
background-image: radial-gradient(var(--va-canvas-dot) 1px, transparent 1px);
background-size: 20px 20px;
}
.va-workbench__canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
}
.va-static {
font-family: var(--mono);
font-size: 1.25rem;
line-height: 1.35;
letter-spacing: 0.08em;
margin: 1rem 0;
padding: 1rem 1.25rem;
color: var(--ink);
background: color-mix(in srgb, var(--paper) 50%, var(--card-bg));
border: 1px solid var(--line);
border-radius: 0.5rem;
overflow-x: auto;
}
.va-static__carry,
.va-static__borrow {
font-size: 0.85em;
vertical-align: super;
}
.book-prose .written-terms-table td:first-child {
font-weight: 600;
white-space: nowrap;
}