From 2554a0cb87b005853e410cb08bed0053e61c2935 Mon Sep 17 00:00:00 2001 From: Kurant Date: Sun, 22 Feb 2026 11:45:41 +0100 Subject: [PATCH 1/6] fix: drop dark mode code block background MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The --shiki-dark-bg variable (e.g. #24292e from github-dark) was adding a visible background to code blocks in dark mode. haspar.us prod had transparent backgrounds — keep it that way. --- src/global-styles/shiki.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/global-styles/shiki.css b/src/global-styles/shiki.css index d988b49..eadb9eb 100644 --- a/src/global-styles/shiki.css +++ b/src/global-styles/shiki.css @@ -8,7 +8,7 @@ html.dark .astro-code-themes { color: var(--shiki-dark) !important; - background-color: var(--shiki-dark-bg) !important; + background-color: transparent !important; } html.dark .astro-code-themes span { From 915849d265f019b09fb6a46e5a5316274845e5d6 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Sun, 22 Feb 2026 12:32:15 +0100 Subject: [PATCH 2/6] Restyle twoslash popup an run format --- .github/workflows/ci.yml | 5 +- src/global-styles/base.css | 6 +-- src/global-styles/integrations.css | 2 +- src/global-styles/shiki.css | 58 ++++++++++++++++++--- src/layouts/BaseLayout.astro | 9 ++-- src/lib/GrainOverlay/GrainOverlay.astro | 2 +- src/lib/Kbd.tsx | 6 +-- src/lib/TableOfContents/PostProgressBar.tsx | 4 +- src/lib/color-scheme/ColorSchemeSelect.tsx | 12 ++--- src/lib/prose/Blockquote.module.css | 2 +- src/lib/prose/Image.astro | 2 + src/lib/prose/Paragraph.module.css | 2 +- src/lib/prose/Table.module.css | 2 +- src/lib/prose/code-and-pre.module.css | 4 +- src/lib/prose/prose.css | 22 +++----- src/lib/titleCase.ts | 2 +- src/pages/[...path].astro | 6 ++- src/pages/admin.astro | 9 ++-- src/pages/index.astro | 15 +++--- 19 files changed, 113 insertions(+), 57 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd61a84..44cd398 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,7 +78,10 @@ jobs: --prod - name: Deploy Preview to Vercel - if: github.ref_name != 'main' + if: + ${{ github.event_name != 'push' && (!github.event.pull_request.head.repo.fork || + (github.event_name == 'pull_request_target' && + contains(github.event.pull_request.labels.*.name, 'trusted'))) }} run: node .github/scripts/deploy.mjs --token=${{ secrets.VERCEL_TOKEN }} - name: Debug Conditions diff --git a/src/global-styles/base.css b/src/global-styles/base.css index 10ad9b7..1a7035c 100644 --- a/src/global-styles/base.css +++ b/src/global-styles/base.css @@ -18,13 +18,11 @@ html { } * { - @apply outline-black dark:outline-white - selection:bg-gray-200 selection:text-black - dark:selection:bg-gray-700 dark:selection:text-white; + @apply outline-black selection:bg-gray-200 selection:text-black dark:outline-white dark:selection:bg-gray-700 dark:selection:text-white; } body { - @apply text-gray-800 dark:text-gray-300 bg-white dark:bg-gray-900 px-0 md:px-4 pt-4 pb-20 md:pt-8 md:pb-48 relative min-h-screen; + @apply relative min-h-screen bg-white px-0 pb-20 pt-4 text-gray-800 dark:bg-gray-900 dark:text-gray-300 md:px-4 md:pb-48 md:pt-8; } body, diff --git a/src/global-styles/integrations.css b/src/global-styles/integrations.css index c15ba74..199c306 100644 --- a/src/global-styles/integrations.css +++ b/src/global-styles/integrations.css @@ -7,6 +7,6 @@ } & ol > li > p { - @apply inline -mx-1; + @apply -mx-1 inline; } } diff --git a/src/global-styles/shiki.css b/src/global-styles/shiki.css index eadb9eb..f37deb1 100644 --- a/src/global-styles/shiki.css +++ b/src/global-styles/shiki.css @@ -46,6 +46,17 @@ pre.twoslash { /* --- Twoslash hover --- */ +@keyframes pop-up { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } +} + .twoslash-hover { border-bottom: 1px dotted transparent; transition-timing-function: ease; @@ -56,21 +67,44 @@ pre.twoslash:hover .twoslash-hover { border-color: #747474; } +:root { + --ease-overshoot-far: linear( + 0 0%, + 0.5007 7.21%, + 0.7803 12.29%, + 0.8883 14.93%, + 0.9724 17.63%, + 1.0343 20.44%, + 1.0754 23.44%, + 1.0898 25.22%, + 1.0984 27.11%, + 1.101 29.15%, + 1.098 31.4%, + 1.085 35.23%, + 1.019 48.86%, + 1.004 54.06%, + 0.9956 59.6%, + 0.9925 68.11%, + 1 100% + ); +} + .twoslash-popup-container { - display: none; + opacity: 0; + scale: 0.95; + pointer-events: none; position: absolute; - transform: translate(0, 1rem); + margin-top: 1rem; z-index: 100; - @apply bg-white text-gray-900 border border-gray-200 shadow-sm px-3 p-2 rounded-sm - dark:bg-black dark:text-gray-50 dark:border-gray-700 dark:shadow-md; + @apply rounded-sm border border-gray-200 bg-white p-2 px-3 text-gray-900 shadow-sm dark:border-gray-700 dark:bg-black dark:text-gray-50 dark:shadow-md; text-align: left; white-space: pre-wrap; } .twoslash-hover:hover > .twoslash-popup-container { - display: block; + animation: pop-up 300ms var(--ease-overshoot-far) forwards 250ms; } .twoslash-popup-code { @@ -79,8 +113,14 @@ pre.twoslash:hover .twoslash-hover { @media (prefers-reduced-motion: reduce) { .twoslash-hover { + opacity: 1; + scale: 1; transition: none; } + + .twoslash-hover:hover > .twoslash-popup-container { + animation: none; + } } /* --- Twoslash errors --- */ @@ -155,7 +195,13 @@ pre .error-behind { } .tag-container .twoslash-annotation { position: absolute; - font-family: "JetBrains Mono", Menlo, Monaco, Consolas, Courier New, monospace; + font-family: + "JetBrains Mono", + Menlo, + Monaco, + Consolas, + Courier New, + monospace; right: -10px; width: 200px; color: #187abf; diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index 2b89dd5..64396f9 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -25,7 +25,10 @@ interface Props { const { title, description, ogImage } = Astro.props; -const postModules = import.meta.glob<{ frontmatter: PostFrontmatter }>("../../posts/**/*.mdx", { eager: true }); +const postModules = import.meta.glob<{ frontmatter: PostFrontmatter }>( + "../../posts/**/*.mdx", + { eager: true }, +); const posts = Object.values(postModules) .filter((p) => (import.meta.env.PROD ? !p.frontmatter.draft : true)) .map((p) => ({ @@ -64,9 +67,9 @@ posts.sort((a, b) => { -
+
diff --git a/src/lib/GrainOverlay/GrainOverlay.astro b/src/lib/GrainOverlay/GrainOverlay.astro index cdf2b7f..692a375 100644 --- a/src/lib/GrainOverlay/GrainOverlay.astro +++ b/src/lib/GrainOverlay/GrainOverlay.astro @@ -10,6 +10,6 @@ const { opacity } = Astro.props;
diff --git a/src/lib/Kbd.tsx b/src/lib/Kbd.tsx index 24d9b74..ab88aa2 100644 --- a/src/lib/Kbd.tsx +++ b/src/lib/Kbd.tsx @@ -18,11 +18,11 @@ export function Kbd(props: KbdProps) { ref={ref} {...props} class={ - "p-1 border border-b-2 rounded-md bg-gray-50" + + "rounded-md border border-b-2 bg-gray-50 p-1" + " dark:border-gray-700 dark:bg-gray-800" + - " tracking-tighter leading-none text-xs" + + " text-xs leading-none tracking-tighter" + " group-hover:border-b group-hover:shadow-[inset_0_1px_1px_0_rgba(0,0,0,0.025)] group-focus:outline" + - " [&[data-pressed]]:border-b [&[data-pressed]]" + + " [&[data-pressed]] [&[data-pressed]]:border-b" + (props.class ? ` ${props.class}` : "") } /> diff --git a/src/lib/TableOfContents/PostProgressBar.tsx b/src/lib/TableOfContents/PostProgressBar.tsx index 96284fc..5b0c4e4 100644 --- a/src/lib/TableOfContents/PostProgressBar.tsx +++ b/src/lib/TableOfContents/PostProgressBar.tsx @@ -30,10 +30,10 @@ export function PostProgressBar(props: { children: JSX.Element }) { return (
-
+
{ - setScheme( - (event.currentTarget.value as "dark" | "light" | "") || null, - ); - }} - style={{ - color: "var(--text-color)", - }} - > - - {(x) => ( - - )} - - - - - -
- ); -} - -function SystemIcon() { - return ( - - - - - ); -} - -function MoonIcon() { - return ( - - - - ); -} - -function SunIcon() { - return ( - - - - ); -} From 52695fe35ce2a38148e9dc96b72bd39a5628cb21 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Thu, 5 Mar 2026 22:47:49 +0100 Subject: [PATCH 4/6] Kill the animation --- src/global-styles/shiki.css | 47 +++---------------------------------- 1 file changed, 3 insertions(+), 44 deletions(-) diff --git a/src/global-styles/shiki.css b/src/global-styles/shiki.css index 99721a6..30d943b 100644 --- a/src/global-styles/shiki.css +++ b/src/global-styles/shiki.css @@ -46,17 +46,6 @@ pre.twoslash { /* --- Twoslash hover --- */ -@keyframes pop-up { - from { - opacity: 0; - transform: scale(0.9); - } - to { - opacity: 1; - transform: scale(1); - } -} - .twoslash-hover { border-bottom: 1px dotted transparent; transition-timing-function: ease; @@ -67,34 +56,10 @@ pre.twoslash:hover .twoslash-hover { border-color: #747474; } -:root { - --ease-overshoot-far: linear( - 0 0%, - 0.5007 7.21%, - 0.7803 12.29%, - 0.8883 14.93%, - 0.9724 17.63%, - 1.0343 20.44%, - 1.0754 23.44%, - 1.0898 25.22%, - 1.0984 27.11%, - 1.101 29.15%, - 1.098 31.4%, - 1.085 35.23%, - 1.019 48.86%, - 1.004 54.06%, - 0.9956 59.6%, - 0.9925 68.11%, - 1 100% - ); -} - .twoslash-popup-container { - opacity: 0; - scale: 0.95; - pointer-events: none; + display: none; position: absolute; - margin-top: 1rem; + transform: translate(0, 1rem); z-index: 100; @apply rounded-sm border border-gray-200 bg-white p-2 px-3 text-gray-900 shadow-sm dark:border-gray-700 dark:bg-black dark:text-gray-50 dark:shadow-md; @@ -104,7 +69,7 @@ pre.twoslash:hover .twoslash-hover { } .twoslash-hover:hover > .twoslash-popup-container { - animation: pop-up 300ms var(--ease-overshoot-far) forwards 250ms; + display: block; } .twoslash-popup-code { @@ -113,14 +78,8 @@ pre.twoslash:hover .twoslash-hover { @media (prefers-reduced-motion: reduce) { .twoslash-hover { - opacity: 1; - scale: 1; transition: none; } - - .twoslash-hover:hover > .twoslash-popup-container { - animation: none; - } } /* --- Twoslash errors --- */ From 29696af63b7c59dac53bb7c8b45b3ef7cde4443a Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Thu, 5 Mar 2026 23:03:08 +0100 Subject: [PATCH 5/6] fix: use CSS anchor positioning for twoslash tooltips Replaces absolute positioning with CSS Anchor Positioning API so tooltips escape the pre's overflow clipping and center on the hovered token. Includes a fallback to flip above when near the bottom of the viewport. --- src/global-styles/shiki.css | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/global-styles/shiki.css b/src/global-styles/shiki.css index 30d943b..ddc23ff 100644 --- a/src/global-styles/shiki.css +++ b/src/global-styles/shiki.css @@ -56,11 +56,19 @@ pre.twoslash:hover .twoslash-hover { border-color: #747474; } +.twoslash-hover:hover { + anchor-name: --twoslash-hover; +} + .twoslash-popup-container { display: none; - position: absolute; - transform: translate(0, 1rem); + position: fixed; + position-anchor: --twoslash-hover; + position-area: bottom; + position-try-fallbacks: --above; z-index: 100; + width: max-content; + max-width: 80vw; @apply rounded-sm border border-gray-200 bg-white p-2 px-3 text-gray-900 shadow-sm dark:border-gray-700 dark:bg-black dark:text-gray-50 dark:shadow-md; @@ -68,6 +76,10 @@ pre.twoslash:hover .twoslash-hover { white-space: pre-wrap; } +@position-try --above { + position-area: top; +} + .twoslash-hover:hover > .twoslash-popup-container { display: block; } From 8eb109af05b71c82d428054b7a43ab141b2922d5 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Thu, 5 Mar 2026 23:18:13 +0100 Subject: [PATCH 6/6] fix: add fallback for browsers without CSS anchor positioning --- src/global-styles/shiki.css | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/global-styles/shiki.css b/src/global-styles/shiki.css index ddc23ff..c0f0e8b 100644 --- a/src/global-styles/shiki.css +++ b/src/global-styles/shiki.css @@ -56,16 +56,10 @@ pre.twoslash:hover .twoslash-hover { border-color: #747474; } -.twoslash-hover:hover { - anchor-name: --twoslash-hover; -} - .twoslash-popup-container { display: none; - position: fixed; - position-anchor: --twoslash-hover; - position-area: bottom; - position-try-fallbacks: --above; + position: absolute; + transform: translate(0, 1rem); z-index: 100; width: max-content; max-width: 80vw; @@ -76,8 +70,22 @@ pre.twoslash:hover .twoslash-hover { white-space: pre-wrap; } -@position-try --above { - position-area: top; +@supports (anchor-name: --x) { + .twoslash-hover:hover { + anchor-name: --twoslash-hover; + } + + .twoslash-popup-container { + position: fixed; + transform: none; + position-anchor: --twoslash-hover; + position-area: bottom; + position-try-fallbacks: --above; + } + + @position-try --above { + position-area: top; + } } .twoslash-hover:hover > .twoslash-popup-container {