image modals for posts

This commit is contained in:
2026-05-13 20:42:31 -07:00
parent a56ca4c03e
commit e615e6a91f
4 changed files with 113 additions and 2 deletions

View File

@ -4,7 +4,9 @@ layout: base.njk
{% css %}{% include "css/post.css" %}{% endcss %} {% css %}{% include "css/post.css" %}{% endcss %}
{% css %}{% include "css/highlighting.css" %}{% endcss %} {% css %}{% include "css/highlighting.css" %}{% endcss %}
{% css %}{% include "css/lists.css" %}{% endcss %} {% css %}{% include "css/lists.css" %}{% endcss %}
{% css %}{% include "css/modal.css" %}{% endcss %}
{% js %}{% include "node_modules/@zachleat/heading-anchors/heading-anchors.js" %}{% endjs %} {% js %}{% include "node_modules/@zachleat/heading-anchors/heading-anchors.js" %}{% endjs %}
{% js %}{% include "js/modal.js" %}{% endjs %}
<heading-anchors content="<i class='fa-solid fa-anchor'></i>"> <heading-anchors content="<i class='fa-solid fa-anchor'></i>">
<article> <article>
@ -30,7 +32,11 @@ layout: base.njk
</div> </div>
{% if image %} {% if image %}
<img src="/img/{{ image.src }}" alt="{{ image.alt }}"> <dialog closedby="any" aria-label="image modal" tabindex="-1">
<button class="close-dialog" autofocus aria-label="close the image modal">&times;</button>
<img class="modal-img" src="/img/{{ image.src }}" alt="{{ image.alt }}">
</dialog>
<img tabindex="0" class="hero" src="/img/{{ image.src }}" alt="{{ image.alt }}">
{% endif %} {% endif %}
{{ content | safe }} {{ content | safe }}

View File

@ -178,7 +178,6 @@ span.ha-placeholder {
opacity: .55; opacity: .55;
} }
/* Lists */ /* Lists */
::marker { ::marker {
color: var(--color-pink); color: var(--color-pink);

84
css/modal.css Normal file
View File

@ -0,0 +1,84 @@
img[tabindex="0"]:focus-visible {
outline: .15rem solid var(--color-teal);
}
dialog {
margin: auto;
flex-flow: column;
background: transparent;
border: none;
position: absolute;
}
dialog[open] {
display: flex;
}
dialog::backdrop {
background-color: rgba(from var(--color-bg) r g b / .8);
backdrop-filter: blur(4px);
}
body:has(dialog[open]) {
overflow: hidden;
}
.close-dialog {
background-color: rgba(from var(--color-teal) r g b / .2);
font-size: 1.5rem;
padding: 0 .5rem .15rem;
border-radius: 1rem;
color: var(--color-teal);
box-shadow: .15rem .15rem var(--color-shadow);
border: .08rem solid var(--color-teal);
align-self: flex-end;
justify-self: flex-start;
margin: 0 .1rem;
/* Click animation handling */
position: relative;
top: 1rem;
left: -.15rem;
transition: top .1s ease-in, left .1s ease-in;
}
.close-dialog:focus-visible {
outline: none;
background-color: var(--color-teal);
color: var(--color-bg);
}
@media (any-hover: hover) {
.close-dialog:hover {
outline: none;
background-color: var(--color-teal);
color: var(--color-bg);
}
}
@media (forced-colors: active) {
.close-dialog:focus-visible {
outline-offset: .08rem;
outline: .08rem solid;
}
@media (any-hover: hover) {
.close-dialog:hover {
outline-offset: .08rem;
outline: .08rem solid;
}
}
}
/* Click animation */
.close-dialog:active {
top: 1.1rem;
left: -.05rem;
box-shadow: .05rem .05rem var(--color-shadow);
}
.modal-img {
max-height: calc(90vh - 2rem);
object-fit: contain;
width: auto;
margin: 0 1rem;
}

22
js/modal.js Normal file
View File

@ -0,0 +1,22 @@
/* don't even bother on mobile */
if (window.innerWidth > 650) {
const dialog = document.querySelector("dialog");
const closeButton = document.querySelector(".close-dialog");
const hero = document.querySelector(".hero");
hero.addEventListener("click", (e) => dialog.showModal());
hero.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
dialog.showModal();
}
});
closeButton.addEventListener("click", (e) => dialog.close());
closeButton.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
dialog.close();
}
});
}