From 405094cc356cc76b2289198e4c4ace2afb7974e1 Mon Sep 17 00:00:00 2001 From: janezd Date: Tue, 7 Jul 2026 22:36:31 +0200 Subject: [PATCH 1/3] Fix retina images in side notes --- components/MdxContent.tsx | 17 +++++++++++++++++ styles/globals.scss | 6 +----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/components/MdxContent.tsx b/components/MdxContent.tsx index c5b94c0..508c766 100644 --- a/components/MdxContent.tsx +++ b/components/MdxContent.tsx @@ -33,6 +33,23 @@ export const MdxContent = ({content, chapterId, bookId, t, env, allAnswers}: { env?: Record; allAnswers?: AnswersInBook; }) => { + React.useEffect(() => { + const resize = (img: HTMLImageElement) => { + // Set only maxHeight to avoid overriding maxWidth set to images in side columns + img.style.maxHeight = `${img.naturalHeight / 2}px`; + }; + + const images = document.querySelectorAll("img.retina"); + images.forEach(img => { + if (img.complete) { + resize(img); + } + else { + img.addEventListener("load", () => resize(img), {once: true}); + } + }); + }, []); + if (!content) { return; } diff --git a/styles/globals.scss b/styles/globals.scss index b379e40..ac4ea5b 100644 --- a/styles/globals.scss +++ b/styles/globals.scss @@ -120,10 +120,6 @@ $gap: var(--gap); border-radius: 5px; } - img.retina { - zoom: 0.5; - } - &::after { content: ""; position: absolute; @@ -206,7 +202,7 @@ div.expanding-side-img-container div.overlay { content: ""; position: absolute; top: auto; - bottom: 0px; + bottom: 0; left: 0; width: 32px; height: 32px; From 940054fcb6f48ba7c172a45cd84a040caf4fcab1 Mon Sep 17 00:00:00 2001 From: janezd Date: Tue, 7 Jul 2026 23:13:44 +0200 Subject: [PATCH 2/3] Expanded side images: Improve captions --- components/Book/Sidenote.tsx | 22 ++++++++++----------- styles/globals.scss | 37 +++++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/components/Book/Sidenote.tsx b/components/Book/Sidenote.tsx index 12c810b..77a3ca9 100644 --- a/components/Book/Sidenote.tsx +++ b/components/Book/Sidenote.tsx @@ -140,17 +140,17 @@ export const ExpandingSideImg = ({src, alt, retina, caption, children}: {
- {alt - {(caption || children) && -
- {caption &&
{caption}
} - {children &&
{children}
} -
- } + {alt + {(caption || children) && +
+ {caption &&
{caption}
} + {children &&
{children}
} +
+ }
diff --git a/styles/globals.scss b/styles/globals.scss index ac4ea5b..8abc0a7 100644 --- a/styles/globals.scss +++ b/styles/globals.scss @@ -117,6 +117,7 @@ $gap: var(--gap); object-fit: contain; img { + border: 1px solid #bbb; border-radius: 5px; } @@ -179,15 +180,11 @@ div.expanding-side-img-container div.overlay { img { width: auto; height: auto; - display: block; margin: 0 !important; - border: 1px solid gray; + border: 1px solid #bbb; + border-radius: 8px; } - .caption-cont:has(.no-children) { - visibility: hidden; - } - .children { padding: 0 16px; width: 100%; @@ -198,7 +195,7 @@ div.expanding-side-img-container div.overlay { font-weight: 800 !important; } - .figure .caption-cont::after { + .figure .caption-cont:has(.children)::after { content: ""; position: absolute; top: auto; @@ -224,10 +221,10 @@ div.expanding-side-img-container div.overlay { background-color: #fff4; } - .caption-cont { + .caption-cont:has(.children) { position: absolute; bottom: 0; - background-color: white; + background-color: #fff9; overflow: visible; text-overflow: unset; height: 32px; @@ -256,8 +253,26 @@ div.expanding-side-img-container:has(:hover) .overlay, div.expanding-side-img-co pointer-events: none; .figure img { - max-width: 90vw; - max-height: 90vh; + max-width: 98vw; + max-height: 85vh; + } + + .figure:has(.children) img { + max-height: 98vh; + } + + .caption-cont:has(.no-children) { + visibility: visible; + position: fixed; + bottom: 0; + left: 0; + width: 100vw; + padding: 12px; + background-color: #333a; + color: white; + display: flex; + align-content: center; + justify-content: center; } } From deaca9760f78b204e61dc3c8d3205448676f7f5b Mon Sep 17 00:00:00 2001 From: janezd Date: Wed, 8 Jul 2026 14:32:19 +0200 Subject: [PATCH 3/3] Refactor css for expanding side images --- components/Book/Sidenote.tsx | 7 +- components/Book/expanding-side-img.scss | 240 ++++++++++++++++++++++++ styles/globals.scss | 192 +------------------ styles/variables.scss | 19 ++ 4 files changed, 268 insertions(+), 190 deletions(-) create mode 100644 components/Book/expanding-side-img.scss create mode 100644 styles/variables.scss diff --git a/components/Book/Sidenote.tsx b/components/Book/Sidenote.tsx index 77a3ca9..301a16f 100644 --- a/components/Book/Sidenote.tsx +++ b/components/Book/Sidenote.tsx @@ -1,5 +1,6 @@ "use client"; +import "./expanding-side-img.scss" import React, { ReactNode, createContext, useRef, useCallback, useMemo } from "react"; import Image from "@/components/Image"; @@ -134,7 +135,7 @@ export const ExpandingSideImg = ({src, alt, retina, caption, children}: { className={retina ? " retina" : ""} />
- {caption &&
{caption}
} + {caption &&

{caption}

} {children &&
{children}
}
@@ -146,8 +147,8 @@ export const ExpandingSideImg = ({src, alt, retina, caption, children}: { className={retina ? " retina" : ""} /> {(caption || children) && -
- {caption &&
{caption}
} +
+ {caption &&
{caption}
} {children &&
{children}
}
} diff --git a/components/Book/expanding-side-img.scss b/components/Book/expanding-side-img.scss new file mode 100644 index 0000000..de3032b --- /dev/null +++ b/components/Book/expanding-side-img.scss @@ -0,0 +1,240 @@ +@use "../../styles/variables" as *; + +@keyframes show-icon { + 0% { opacity: 0; } + 25% { opacity: 1; } + 50% { opacity: 0; } + 75% { opacity: 1; } + 100% { opacity: 0.7; } +} + +div.expanding-side-img-container { + div.expanding-side-img { + margin: 0; + position: absolute; + width: $aside-width; + transform: translateX(calc(-100% - $gap)); + + .caption { + padding-top: 0px; + } + + .caption p { + font-weight: 400; + } + } + + img { + margin: 0; + border-radius: 8px; + } + + div.overlay { + visibility: hidden; + + position: fixed; + top: 50%; + left: 50%; + width: 10vw; + height: 10vh; + transition: width 0.2s, height 0.2s; + transform: translate(-50%, -50%); + + text-align: center; + z-index: 100; + + display: flex; + justify-content: center; + align-items: center; + + .figure { + display: inline-block; + width: auto; + position: relative; + margin: auto; + margin-top: 0; + margin-bottom: 0; + overflow: hidden; + + &:has(.children) img { + max-height: 98vh; + } + + img { + width: auto; + height: auto; + max-width: 98vw; + max-height: 85vh; + } + + &:hover .caption-overlay:has(.children)::after { + animation: show-icon 2s ease both; + } + + .caption-overlay { + .children { + padding: 0 16px; + width: 100%; + text-align: left; + } + + .caption p { + padding: 0 16px 0 16px; + font-weight: 800; + } + + &:has(.children) { + position: absolute; + bottom: 0; + max-height: 32px; + overflow: hidden; + + background-color: #fff9; + text-overflow: unset; + transition: background-color 0.5s ease, max-height 0.5s ease; + + &::after { + content: ""; + position: absolute; + top: auto; + bottom: 0; + left: 0; + width: 32px; + height: 32px; + opacity: 0; + + background-color: rgb(80 80 80 / 70%); + background-image: url("/icons/eye.svg"); + background-repeat: no-repeat; + background-position: center; + background-size: 18px; + + border-radius: 4px; + } + + &:hover { + background-color: #fffd; + padding: 16px 16px 0 16px; + max-height: 100vh; + + &::after { + opacity: 0 !important; + } + } + } + } + } + } +} + +@media ( min-width: 681px ) { + div.expanding-side-img-container { + &:has(:hover) .overlay { + width: 100vw; + height: 100vh; + background-color: #dddd; + visibility: visible; + pointer-events: none; + + .caption-overlay:not(:has(.children)) { + visibility: visible; + position: fixed; + bottom: 0; + left: 0; + width: 100vw; + padding: 12px; + background-color: #333a; + color: white; + display: flex; + align-content: center; + justify-content: center; + } + } + + &:has(:hover) .figure { + opacity: 1; + pointer-events: auto; + } + } + .expanding-side-img img, .overlay .figure img { + border: 1px solid #bbb; + } + + div.expanding-side-img { + transform-origin: top left; + max-width: $aside-width; + padding: 0; + object-fit: contain; + + img { + border-radius: 5px; + } + + &::after { + content: ""; + position: absolute; + top: 0; + right: 0; + width: 32px; + height: 32px; + + background-color: rgb(80 80 80 / 70%); + background-image: url("/icons/magnifying-glass.svg"); + background-repeat: no-repeat; + background-position: center; + background-size: 18px; + + border-radius: 4px; + } + + .caption-cont { + .caption { + padding-top: 12px; + + } + + .children { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + } + } + } +} + + +@media print, ( max-width: 680px ) { + div.expanding-side-img-container { + max-width: 100vw; + height: auto; + overflow: clip; + + .overlay { + display: none !important; + } + + .expanding-side-img { + img { + width: 100% + } + &:has(.caption) { + text-align: center; + + &::after { + display: none; + } + + img { + margin-bottom: 0; + } + + .caption, .children { + font-weight: 400 !important; + padding: 8px 0 16px 0; + } + } + } + } +} diff --git a/styles/globals.scss b/styles/globals.scss index 8abc0a7..0b0fbbd 100644 --- a/styles/globals.scss +++ b/styles/globals.scss @@ -1,4 +1,5 @@ @use "./colors" as *; +@use "./variables" as *; @use "quiz/quiz" as quiz; /*@use "admin/admin" as admin; */ @use "content-index/content-index" as *; @@ -80,7 +81,7 @@ $gap: var(--gap); } @media ( min-width: 681px ) { - [data-float-aside] + p, [data-float-aside] + div, div.float-aside, div.expanding-side-img { + [data-float-aside] + p, [data-float-aside] + div, div.float-aside { margin: 0 !important; position: absolute; width: $aside-width; @@ -109,198 +110,15 @@ $gap: var(--gap); ) !important; margin-right: auto; } - - div.expanding-side-img { - transform-origin: top left; - max-width: $aside-width; - padding: 0; - object-fit: contain; - - img { - border: 1px solid #bbb; - border-radius: 5px; - } - - &::after { - content: ""; - position: absolute; - top: 8px; - right: 0; - width: 32px; - height: 32px; - - background-color: rgb(80 80 80 / 70%); - background-image: url("/icons/magnifying-glass.svg"); - background-repeat: no-repeat; - background-position: center; - background-size: 18px; - - border-radius: 4px; - } - - .caption-cont .children { - display: -webkit-box; - -webkit-line-clamp: 2; // number of lines - -webkit-box-orient: vertical; - overflow: hidden; - text-overflow: ellipsis; - } - } -} - -div.expanding-side-img-container div.overlay { - visibility: hidden; - - position: fixed; - top: 50% !important; - left: 50% !important; - width: 10vw; - height: 10vh; - transition: width 0.2s, height 0.2s; - transform: translate(-50%, -50%); - - text-align: center; - z-index: 100; - - inset: 0; - display: flex; - justify-content: center; - align-items: center; - - .figure { - display: inline-block; - width: auto; - position: relative; - margin: auto; - margin-top: 0 !important; - margin-bottom: 0 !important; - overflow: hidden; - } - - img { - width: auto; - height: auto; - margin: 0 !important; - border: 1px solid #bbb; - border-radius: 8px; - } - - .children { - padding: 0 16px; - width: 100%; - } - - .caption.with-children { - padding: 0px 16px 0 16px; - font-weight: 800 !important; - } - - .figure .caption-cont:has(.children)::after { - content: ""; - position: absolute; - top: auto; - bottom: 0; - left: 0; - width: 32px; - height: 32px; - - background-color: rgb(80 80 80 / 70%); - background-image: url("/icons/eye.svg"); - background-repeat: no-repeat; - background-position: center; - background-size: 18px; - - border-radius: 4px; - } - - .figure .caption-cont:has(:hover)::after { - opacity: 0 - } - - .caption-cont:has(.children) { - background-color: #fff4; - } - - .caption-cont:has(.children) { - position: absolute; - bottom: 0; - background-color: #fff9; - overflow: visible; - text-overflow: unset; - height: 32px; - } - - .caption-cont:hover { - transition: opacity 0.2s linear; - background-color: #fffd; - padding: 16px 16px 0 16px; - height: auto; - } - - .children { - text-align: left; - } -} - -div.expanding-side-img-container:has(:hover) .overlay, div.expanding-side-img-container.overlay:has(.figure:hover) { - width: 100vw; - height: 100vh; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background-color: #dddd; - visibility: visible; - pointer-events: none; - - .figure img { - max-width: 98vw; - max-height: 85vh; - } - - .figure:has(.children) img { - max-height: 98vh; - } - - .caption-cont:has(.no-children) { - visibility: visible; - position: fixed; - bottom: 0; - left: 0; - width: 100vw; - padding: 12px; - background-color: #333a; - color: white; - display: flex; - align-content: center; - justify-content: center; - } -} - -div.expanding-side-img-container:has(:hover) .figure, div.expanding-side-img-container.overlay .figure:hover { - opacity: 1; - pointer-events: visible; } @media print, ( max-width: 680px ) { - img, div.expanding-side-img { + img { max-width: $prose-width; height: auto; overflow: clip; } - img:has(+ .caption) { - margin-bottom: 12px; - } - - .expanding-side-img:has(.caption) { - margin-bottom: 42px; - } - - .caption.no-children, .caption.with-children, .children { - font-weight: 400 !important; - font-style: italic; - } - .expressive-code { max-width: $prose-width !important; width: $prose-width !important; @@ -335,7 +153,7 @@ div.expanding-side-img-container:has(:hover) .figure, div.expanding-side-img-con margin-top: 0! important; } - .expanding-side-img img, .float-aside img { + .float-aside img { width: 100% !important; } @@ -383,7 +201,7 @@ div.expanding-side-img-container:has(:hover) .figure, div.expanding-side-img-con :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *))::before, :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *))::after { - content: ""; + content: ""; } strong { diff --git a/styles/variables.scss b/styles/variables.scss new file mode 100644 index 0000000..43e4191 --- /dev/null +++ b/styles/variables.scss @@ -0,0 +1,19 @@ + +$img-margin: 8px; +$max-size: 140; + +:root { + --aside-width: 260px; + --prose-width: 940px; + --gap: 32px; + + @media (max-width: 680px) { + --aside-width: 0; + --prose-width: 100vw; // Using 100% allows some elements to expand + --gap: 0; + } +} + +$aside-width: var(--aside-width); +$prose-width: var(--prose-width); +$gap: var(--gap);