refactor(css): add text-2xs and small-caps utilities, replace inline styles
- Add @utility text-2xs (0.65rem) and small-caps (font-variant) to input.css - Replace inline style="font-variant: small-caps" with .small-caps class across all list partials and base.html footer - Add gap support to .tab and .tab-mobile for icon+label layout - Reduce mobile top padding (py-4 md:py-10) on main container - Rename CSS section header from "Scrollbar hide" to "Utilities"
This commit is contained in:
parent
0258ca6840
commit
af9344202d
9 changed files with 106 additions and 70 deletions
|
|
@ -88,7 +88,7 @@ a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Scrollbar hide ───────────────────────────────────────────── */
|
/* ── Utilities ────────────────────────────────────────────────── */
|
||||||
|
|
||||||
@utility scrollbar-hide {
|
@utility scrollbar-hide {
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
|
|
@ -98,6 +98,15 @@ a {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@utility text-2xs {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
line-height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility small-caps {
|
||||||
|
font-variant: small-caps;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Form controls ─────────────────────────────────────────────── */
|
/* ── Form controls ─────────────────────────────────────────────── */
|
||||||
|
|
||||||
.input-field {
|
.input-field {
|
||||||
|
|
@ -319,6 +328,7 @@ input.input-field[type="number"] {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
gap: 0.375rem;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
padding: 0.375rem 0.875rem;
|
padding: 0.375rem 0.875rem;
|
||||||
|
|
@ -340,7 +350,9 @@ input.input-field[type="number"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-mobile {
|
.tab-mobile {
|
||||||
display: block;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.375rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,25 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en" data-star-root>
|
<html lang="en" data-star-root>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="{% block description %}Self-hosted coffee logging — track roasters, roasts, bags, brews, and gear.{% endblock %}"
|
||||||
|
/>
|
||||||
<title>{% block title %}Brewlog{% endblock %}</title>
|
<title>{% block title %}Brewlog{% endblock %}</title>
|
||||||
<link rel="stylesheet" href="/styles.css" />
|
<link rel="stylesheet" href="/styles.css" />
|
||||||
<link rel="icon" id="favicon" type="image/svg+xml" href="/favicon-light.svg" />
|
<link rel="icon" id="favicon" type="image/svg+xml" href="/favicon-light.svg" />
|
||||||
<script>
|
<script>
|
||||||
(() => {
|
;(() => {
|
||||||
const stored = localStorage.getItem("theme");
|
const stored = localStorage.getItem("theme")
|
||||||
const isDark = stored === "dark" || (!stored && matchMedia("(prefers-color-scheme: dark)").matches);
|
const isDark =
|
||||||
|
stored === "dark" || (!stored && matchMedia("(prefers-color-scheme: dark)").matches)
|
||||||
if (isDark) {
|
if (isDark) {
|
||||||
document.documentElement.setAttribute("data-theme", "dark");
|
document.documentElement.setAttribute("data-theme", "dark")
|
||||||
document.getElementById("favicon").href = "/favicon-dark.svg";
|
document.getElementById("favicon").href = "/favicon-dark.svg"
|
||||||
}
|
}
|
||||||
})();
|
})()
|
||||||
</script>
|
</script>
|
||||||
<script
|
<script
|
||||||
type="module"
|
type="module"
|
||||||
|
|
@ -25,98 +30,117 @@
|
||||||
{% block head %}{% endblock %}
|
{% block head %}{% endblock %}
|
||||||
<script>
|
<script>
|
||||||
const toggleRow = (event) => {
|
const toggleRow = (event) => {
|
||||||
if (event.target.closest('.card-detail')) return;
|
if (event.target.closest(".card-detail")) return
|
||||||
const row = event.currentTarget;
|
const row = event.currentTarget
|
||||||
row.classList.toggle('expanded');
|
row.classList.toggle("expanded")
|
||||||
row.querySelector('.card-detail')?.classList.toggle('hidden');
|
row.querySelector(".card-detail")?.classList.toggle("hidden")
|
||||||
const r = row.nextElementSibling;
|
const r = row.nextElementSibling
|
||||||
if (r?.classList.contains('detail-row')) r.classList.toggle('hidden');
|
if (r?.classList.contains("detail-row")) r.classList.toggle("hidden")
|
||||||
row.querySelector('.icon-expand')?.classList.toggle('hidden');
|
row.querySelector(".icon-expand")?.classList.toggle("hidden")
|
||||||
row.querySelector('.icon-collapse')?.classList.toggle('hidden');
|
row.querySelector(".icon-collapse")?.classList.toggle("hidden")
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const mobileQuery = window.matchMedia("(max-width: 767px)");
|
const mobileQuery = window.matchMedia("(max-width: 767px)")
|
||||||
if (!mobileQuery.matches) return;
|
if (!mobileQuery.matches) return
|
||||||
|
|
||||||
const setupInfiniteScroll = () => {
|
const setupInfiniteScroll = () => {
|
||||||
document.querySelectorAll("[data-infinite-scroll]").forEach((container) => {
|
document.querySelectorAll("[data-infinite-scroll]").forEach((container) => {
|
||||||
const sentinel = container.querySelector(".infinite-scroll-sentinel");
|
const sentinel = container.querySelector(".infinite-scroll-sentinel")
|
||||||
if (!sentinel || sentinel.dataset.observed) return;
|
if (!sentinel || sentinel.dataset.observed) return
|
||||||
sentinel.dataset.observed = "true";
|
sentinel.dataset.observed = "true"
|
||||||
|
|
||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
(entries) => {
|
(entries) => {
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
if (!entry.isIntersecting) return;
|
if (!entry.isIntersecting) return
|
||||||
|
|
||||||
const nextUrl = container.getAttribute("data-next-url");
|
const nextUrl = container.getAttribute("data-next-url")
|
||||||
const targetSelector = container.getAttribute("data-target");
|
const targetSelector = container.getAttribute("data-target")
|
||||||
if (!nextUrl || !targetSelector) {
|
if (!nextUrl || !targetSelector) {
|
||||||
observer.disconnect();
|
observer.disconnect()
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
observer.disconnect();
|
observer.disconnect()
|
||||||
sentinel.remove();
|
sentinel.remove()
|
||||||
|
|
||||||
fetch(nextUrl, {
|
fetch(nextUrl, {
|
||||||
headers: { "datastar-request": "true" },
|
headers: { "datastar-request": "true" },
|
||||||
})
|
})
|
||||||
.then((res) => res.text())
|
.then((res) => res.text())
|
||||||
.then((html) => {
|
.then((html) => {
|
||||||
const doc = new DOMParser().parseFromString(html, "text/html");
|
const doc = new DOMParser().parseFromString(html, "text/html")
|
||||||
const newTarget = doc.querySelector(targetSelector);
|
const newTarget = doc.querySelector(targetSelector)
|
||||||
if (!newTarget) return;
|
if (!newTarget) return
|
||||||
|
|
||||||
// Append new rows to existing tbody
|
// Append new rows to existing tbody
|
||||||
const existingTbody = container.querySelector("tbody");
|
const existingTbody = container.querySelector("tbody")
|
||||||
const newTbody = newTarget.querySelector("tbody");
|
const newTbody = newTarget.querySelector("tbody")
|
||||||
if (existingTbody && newTbody) {
|
if (existingTbody && newTbody) {
|
||||||
Array.from(newTbody.children).forEach((row) => {
|
Array.from(newTbody.children).forEach((row) => {
|
||||||
existingTbody.appendChild(document.adoptNode(row));
|
existingTbody.appendChild(document.adoptNode(row))
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if there are more pages
|
// Check if there are more pages
|
||||||
const newContainer = newTarget.querySelector("[data-infinite-scroll]");
|
const newContainer = newTarget.querySelector("[data-infinite-scroll]")
|
||||||
const newNextUrl = newContainer && newContainer.getAttribute("data-next-url");
|
const newNextUrl = newContainer && newContainer.getAttribute("data-next-url")
|
||||||
if (newNextUrl) {
|
if (newNextUrl) {
|
||||||
container.setAttribute("data-next-url", newNextUrl);
|
container.setAttribute("data-next-url", newNextUrl)
|
||||||
const newSentinel = document.createElement("div");
|
const newSentinel = document.createElement("div")
|
||||||
newSentinel.className = "infinite-scroll-sentinel h-4 md:hidden";
|
newSentinel.className = "infinite-scroll-sentinel h-4 md:hidden"
|
||||||
newSentinel.setAttribute("aria-hidden", "true");
|
newSentinel.setAttribute("aria-hidden", "true")
|
||||||
container.appendChild(newSentinel);
|
container.appendChild(newSentinel)
|
||||||
setupInfiniteScroll();
|
setupInfiniteScroll()
|
||||||
} else {
|
} else {
|
||||||
container.removeAttribute("data-next-url");
|
container.removeAttribute("data-next-url")
|
||||||
container.removeAttribute("data-infinite-scroll");
|
container.removeAttribute("data-infinite-scroll")
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
{ rootMargin: "200px" }
|
{ rootMargin: "200px" }
|
||||||
);
|
)
|
||||||
|
|
||||||
observer.observe(sentinel);
|
observer.observe(sentinel)
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
setupInfiniteScroll();
|
setupInfiniteScroll()
|
||||||
|
|
||||||
const bodyObserver = new MutationObserver(() => {
|
const bodyObserver = new MutationObserver(() => {
|
||||||
setupInfiniteScroll();
|
setupInfiniteScroll()
|
||||||
});
|
})
|
||||||
bodyObserver.observe(document.body, { childList: true, subtree: true });
|
bodyObserver.observe(document.body, { childList: true, subtree: true })
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body class="min-h-screen bg-page text-text">
|
<body class="min-h-screen bg-page text-text">
|
||||||
<main class="mx-auto flex w-full max-w-5xl flex-col gap-8 px-6 py-10">
|
<main class="mx-auto flex w-full max-w-5xl flex-col gap-8 px-6 py-4 md:py-10">
|
||||||
{% include "partials/nav.html" %} {% block content %}{% endblock %}
|
{% include "partials/nav.html" %} {% block content %}{% endblock %}
|
||||||
<footer class="mt-8 text-center text-xs tracking-widest text-text-muted" style="font-variant: small-caps">
|
<footer
|
||||||
<p>B{rew}log by <a href="https://jnsgr.uk" class="text-text-muted hover:text-accent transition" target="_blank" rel="noreferrer noopener">jnsgruk</a> · <a href="https://github.com/jnsgruk/brewlog/commit/{{ version_info.commit }}" class="text-text-muted hover:text-accent transition" target="_blank" rel="noreferrer noopener">{{ version_info.commit }}</a></p>
|
class="mt-8 text-center text-xs tracking-widest text-text-muted small-caps"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
B{rew}log by
|
||||||
|
<a
|
||||||
|
href="https://jnsgr.uk"
|
||||||
|
class="text-text-muted hover:text-accent transition"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener"
|
||||||
|
>jnsgruk</a
|
||||||
|
>
|
||||||
|
·
|
||||||
|
<a
|
||||||
|
href="https://github.com/jnsgruk/brewlog/commit/{{ version_info.commit }}"
|
||||||
|
class="text-text-muted hover:text-accent transition"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener"
|
||||||
|
>{{ version_info.commit }}</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
{{ table::search_header(navigator, "#bag-list") }}
|
{{ table::search_header(navigator, "#bag-list") }}
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
||||||
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text" style="font-variant: small-caps">
|
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text small-caps">
|
||||||
<tr>
|
<tr>
|
||||||
{{ table::sortable_header("Added", "created-at", navigator, "#bag-list") }}
|
{{ table::sortable_header("Added", "created-at", navigator, "#bag-list") }}
|
||||||
{{ table::sortable_header("Roaster", "roaster", navigator, "#bag-list") }}
|
{{ table::sortable_header("Roaster", "roaster", navigator, "#bag-list") }}
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
<div class="h-1.5 rounded-full bg-surface-alt overflow-hidden">
|
<div class="h-1.5 rounded-full bg-surface-alt overflow-hidden">
|
||||||
<div class="h-full rounded-full bg-accent" style="width: {{ bag.used_percent }}%"></div>
|
<div class="h-full rounded-full bg-accent" style="width: {{ bag.used_percent }}%"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-0.5 text-text-muted" style="font-variant: small-caps; font-size: 0.7rem">{{ bag.remaining }} / {{ bag.amount }}g</div>
|
<div class="mt-0.5 text-text-muted small-caps" style="font-size: 0.7rem">{{ bag.remaining }} / {{ bag.amount }}g</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
<div class="h-1.5 rounded-full bg-surface-alt overflow-hidden">
|
<div class="h-1.5 rounded-full bg-surface-alt overflow-hidden">
|
||||||
<div class="h-full rounded-full bg-accent" style="width: {{ bag.used_percent }}%"></div>
|
<div class="h-full rounded-full bg-accent" style="width: {{ bag.used_percent }}%"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-0.5 text-right text-text-muted" style="font-variant: small-caps; font-size: 0.7rem">{{ bag.remaining }} / {{ bag.amount }}g</div>
|
<div class="mt-0.5 text-right text-text-muted small-caps" style="font-size: 0.7rem">{{ bag.remaining }} / {{ bag.amount }}g</div>
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td data-label="Finished" class="mobile-hidden whitespace-nowrap px-4 py-3 font-medium text-text-secondary">
|
<td data-label="Finished" class="mobile-hidden whitespace-nowrap px-4 py-3 font-medium text-text-secondary">
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
|
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
||||||
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text" style="font-variant: small-caps">
|
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text small-caps">
|
||||||
<tr>
|
<tr>
|
||||||
{{ table::sortable_header("Added", "created-at", navigator, "#brew-list") }}
|
{{ table::sortable_header("Added", "created-at", navigator, "#brew-list") }}
|
||||||
<th scope="col" class="px-4 py-3">Coffee</th>
|
<th scope="col" class="px-4 py-3">Coffee</th>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
{{ table::search_header(navigator, "#cafe-list") }}
|
{{ table::search_header(navigator, "#cafe-list") }}
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
||||||
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text" style="font-variant: small-caps">
|
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text small-caps">
|
||||||
<tr>
|
<tr>
|
||||||
{{ table::sortable_header("Added", "created-at", navigator, "#cafe-list") }}
|
{{ table::sortable_header("Added", "created-at", navigator, "#cafe-list") }}
|
||||||
{{ table::sortable_header("Name", "name", navigator, "#cafe-list") }}
|
{{ table::sortable_header("Name", "name", navigator, "#cafe-list") }}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
{{ table::search_header(navigator, "#cup-list") }}
|
{{ table::search_header(navigator, "#cup-list") }}
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
||||||
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text" style="font-variant: small-caps">
|
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text small-caps">
|
||||||
<tr>
|
<tr>
|
||||||
{{ table::sortable_header("Added", "created-at", navigator, "#cup-list") }}
|
{{ table::sortable_header("Added", "created-at", navigator, "#cup-list") }}
|
||||||
{{ table::sortable_header("Roast", "roast", navigator, "#cup-list") }}
|
{{ table::sortable_header("Roast", "roast", navigator, "#cup-list") }}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
||||||
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text" style="font-variant: small-caps">
|
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text small-caps">
|
||||||
<tr>
|
<tr>
|
||||||
{{ table::sortable_header("Added", "created-at", navigator, "#gear-list") }}
|
{{ table::sortable_header("Added", "created-at", navigator, "#gear-list") }}
|
||||||
{{ table::sortable_header("Category", "category", navigator, "#gear-list") }}
|
{{ table::sortable_header("Category", "category", navigator, "#gear-list") }}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
{{ table::search_header(navigator, "#roast-list") }}
|
{{ table::search_header(navigator, "#roast-list") }}
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
||||||
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text" style="font-variant: small-caps">
|
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text small-caps">
|
||||||
<tr>
|
<tr>
|
||||||
{{ table::sortable_header("Added", "created-at", navigator, "#roast-list") }}
|
{{ table::sortable_header("Added", "created-at", navigator, "#roast-list") }}
|
||||||
{{ table::sortable_header("Roast", "name", navigator, "#roast-list") }}
|
{{ table::sortable_header("Roast", "name", navigator, "#roast-list") }}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
{{ table::search_header(navigator, "#roaster-list") }}
|
{{ table::search_header(navigator, "#roaster-list") }}
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
<table class="responsive-table min-w-full divide-y text-left text-sm text-text">
|
||||||
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text" style="font-variant: small-caps">
|
<thead class="bg-surface-alt text-xs font-semibold tracking-wide text-text small-caps">
|
||||||
<tr>
|
<tr>
|
||||||
{{ table::sortable_header("Added", "created-at", navigator, "#roaster-list") }}
|
{{ table::sortable_header("Added", "created-at", navigator, "#roaster-list") }}
|
||||||
{{ table::sortable_header("Name", "name", navigator, "#roaster-list") }}
|
{{ table::sortable_header("Name", "name", navigator, "#roaster-list") }}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue