website
git clone https://git.pyrossh.dev/website
木 Personal website of pyrossh. Built with astrojs, shiki, vite.
assets/css/repos.css
/* Repo browsing pages (readme/files/commits/blame). Restored from the
original Astro-era RepoLayout/FileLayout/Commit/RecursiveList components
(see git history at 7325531^), with the tab nav restyled as a bordered
tab bar and a couple of small crates.io-inspired touches (clone command
as its own code box, tags as pills) layered on top.
Font sizes on this page follow one small scale instead of one-off
values, so nothing reads as randomly bigger/smaller than its neighbour:
0.85rem — small badges (tags, pull-right meta, file-size hints)
0.9rem — code/monospace content, nav links and sub-headings alike
(pre/code-view, file tree, blame, file title)
0.95rem — box body text (.repo, .event)
var(--fs-repo-title) — page-level heading (.repo h1), fluid 1.3–1.6rem
All of them are rem-based, not em, so nesting depth can't change the
rendered size — that's what made the old code-view/blame font sizes
inconsistent (em compounds with whatever ancestor happens to wrap it).
Everything below uses plain CSS nesting rather than @scope: .repo,
.file-layout, .event, .file-explorer and table.blame are all real,
always-present classes/tags on these pages (none of them are wrapped
in a custom element anymore), so nesting under them scopes their
descendants exactly the way @scope would, without needing a scope root
selector that has to be kept in sync with whatever markup happens to
wrap it. */
.repo {
text-overflow: ellipsis;
overflow: hidden;
padding: 0.5rem;
background: var(--color-box-bg);
color: var(--color-text);
font-size: 0.95rem;
padding: 1rem;
a {
color: var(--color-link);
}
.header {
display: flex;
flex-direction: row;
align-items: baseline;
@media (max-width: 720px) {
flex-direction: column;
align-items: start;
}
}
h1 {
font-size: var(--fs-repo-title);
font-weight: 500;
line-height: 1;
margin-bottom: 0.1rem;
flex: 1;
}
.repo-version {
font-size: 1.1rem;
font-weight: 500;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
color: var(--color-text);
margin-left: 0.5rem;
}
.tags {
margin-top: 0.5rem;
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
}
.tag {
color: var(--color-tag);
padding: 0.15rem 0.5rem 0.5rem 0;
border-radius: 4px;
font-size: 0.95rem;
font-weight: 500;
}
h3.clone-cmd {
display: flex;
align-items: center;
justify-content: space-between;
font-weight: 400;
font-size: var(--fs-repo-clone);
padding: 0.4rem;
background-color: var(--color-pre-bg);
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
overflow-x: auto;
white-space: pre;
width: fit-content;
margin-top: 0.2rem;
.copy-btn {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
width: 1.6rem;
height: 1.6rem;
padding: 0;
border: none;
border-radius: 4px;
background: none;
color: var(--color-text);
opacity: 0.6;
cursor: pointer;
&:hover {
opacity: 1;
background: var(--color-box-bg);
}
.icon-check {
display: none;
}
&.copied {
opacity: 1;
color: var(--color-tag);
.icon-copy {
display: none;
}
.icon-check {
display: block;
}
}
}
}
.headerExtension h2 {
display: flex;
flex: 1;
padding-top: 0.3rem;
font-size: var(--fs-repo-subtitle);
font-weight: 400;
}
hr {
margin-top: 0.5rem;
border: none;
border-top: 1px solid var(--color-text);
}
.nav {
display: flex;
align-items: center;
margin-top: 0.5rem;
span {
padding: 0 0.75rem;
color: var(--color-text);
}
a {
font-size: 1rem;
font-weight: 500;
cursor: pointer;
color: var(--color-link);
text-decoration: none;
&.active-tab {
text-decoration: underline;
text-underline-offset: 4px;
}
}
}
}
#repo-content {
margin-top: 0.75rem;
/* README (repos/repo.njk) — same box treatment as the file explorer
panes below, so it doesn't sit as bare unstyled text against the
page background. Scoped to #repo-content so blog posts (which also
use .markdown-body) are untouched. */
.markdown-body {
background: var(--color-box-bg);
padding: 1rem 1.25rem;
}
}
/* Commit list / commit detail header (Commit.astro) — repos/commit.njk,
commits.njk, file-history.njk. pre.commit is nested here too since
it's always .event's child, never used alone. */
.event {
background-color: var(--color-box-bg);
color: var(--color-text);
text-overflow: ellipsis;
overflow: hidden;
padding: 0.6rem 0.75rem;
margin: 0.5rem 0;
font-size: 0.95rem;
a {
color: var(--color-link);
}
strong {
font-weight: 500;
}
.pull-right {
font-size: 0.85rem;
font-weight: 400;
float: right;
}
pre.commit {
font-family: inherit;
white-space: pre-wrap;
margin-top: 0.35rem;
}
}
/* Two-pane IDE-style file explorer: a persistent tree sidebar + a content
pane, each its own rounded box with a gap between them rather than a
single shared border — the gap is what visually separates them, so
they don't also need to differ in colour. The tree lives outside
#file-pane, so htmx swaps of #file-pane (clicking a file) or
#file-content (Contents/History/Blame) never touch the sidebar — it
just stays where it is, already expanded, no reload.
Fixed-height explorer: both panes stretch to the same height and scroll
independently, so scrolling a long file never scrolls the tree out of
view (or the whole page) — closer to a real IDE split view than a page
that just keeps growing with the file's content. */
.file-explorer {
display: flex;
align-items: stretch;
gap: 0.5rem;
height: 80vh;
/* File tree: flat rows (no per-item card background), a rotating
triangle for expand/collapse instead of an emoji folder glyph, and a
left accent border + tint on the active file instead of a filled
pill. */
.file-explorer-tree {
width: 260px;
flex-shrink: 0;
height: 100%;
overflow-y: auto;
padding: 0.6rem;
background: var(--color-box-bg);
border-radius: 6px;
font-size: 0.9rem;
ul {
list-style: none;
margin: 0 0 0 1.1rem;
padding: 0;
}
> ul {
margin-left: 0;
}
li {
margin: 0;
&.active-file,
& > a.active-file {
color: var(--color-link);
}
}
summary,
li > a {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.2rem 0.35rem;
border-radius: 4px;
border-left: 2px solid transparent;
cursor: pointer;
text-decoration: none;
color: var(--color-text);
line-height: 1.6;
}
summary {
list-style: none;
&::-webkit-details-marker {
display: none;
}
&::before {
content: "";
flex-shrink: 0;
width: 0;
height: 0;
border-style: solid;
border-width: 0.3em 0 0.3em 0.4em;
border-color: transparent transparent transparent currentColor;
opacity: 0.5;
transition: transform 120ms ease;
}
}
details[open] > summary::before {
transform: rotate(90deg);
}
summary svg,
li > a svg {
flex-shrink: 0;
width: 1em;
height: 1em;
}
summary:hover,
li > a:hover {
background-color: var(--color-pre-bg);
}
li > a.active-file {
color: var(--color-link);
background-color: var(--color-highlight-bg);
border-left-color: var(--color-link);
font-weight: 500;
}
.file-size {
margin-left: auto;
opacity: 0.5;
font-size: 0.85rem;
}
}
.file-explorer-main {
flex: 1;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
border: 1px solid var(--color-box-bg);
border-radius: 6px;
overflow: hidden;
&:has(.file-explorer-placeholder) {
align-items: center;
justify-content: center;
}
/* #file-content padding/background: when it holds actual code (a
plain <pre>), let the box-bg colour fill the whole pane
edge-to-edge instead of floating as a small inset card — closer to
a real code viewer's continuous code surface. box-bg (not pre-bg)
so it reads as one calm surface with the file-layout header above
it, rather than the near-black pre-bg used for one-off code
snippets elsewhere on the site. */
#file-content {
flex: 1;
overflow-y: auto;
padding: 0.85rem;
&:has(> pre) {
padding: 0;
background: var(--color-box-bg);
pre {
margin: 0;
background: transparent;
border-radius: 0;
}
}
}
}
.file-explorer-placeholder {
color: var(--color-text);
opacity: 0.6;
padding: 1rem 0;
}
@media (max-width: 720px) {
flex-direction: column;
height: 85vh;
.file-explorer-tree {
width: 100%;
height: auto;
flex: 0 0 35%;
}
.file-explorer-main {
width: 100%;
height: auto;
flex: 1 1 auto;
}
}
}
/* File view header — a compact icon+filename strip with the
Contents/History/Blame tabs, sitting as the card's title bar rather
than a full-width heading. */
.file-layout {
display: flex;
align-items: center;
justify-content: flex-start;
flex-shrink: 0;
gap: 0.5rem;
padding: 0.6rem 0.85rem;
background: var(--color-box-bg);
border-bottom: 1px solid var(--color-pre-bg);
.title {
display: flex;
align-items: center;
gap: 0.4rem;
/* flex:1 (not the default 0 1 auto) so it's the one that gives up
space when the row is tight — without this, a long filename could
force the row to wrap .nav onto its own line instead of shrinking,
since the wrap decision is made off each item's unshrunk
hypothetical width, not its post-shrink width. */
flex: 1 1 auto;
min-width: 0;
font-size: 0.9rem;
font-weight: 500;
margin-bottom: -4px;
svg {
flex-shrink: 0;
width: 1rem;
height: 1rem;
}
span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.nav {
display: flex;
align-items: center;
gap: 0.25rem;
flex-shrink: 0;
div:not(:has(a)) {
display: none;
}
a {
font-size: 0.9rem;
font-weight: 500;
padding: 0.3rem 0.1rem;
margin-right: 0.75rem;
cursor: pointer;
color: var(--color-text);
border-bottom: 2px solid transparent;
&:hover {
color: var(--color-link);
}
&[aria-current="true"] {
color: var(--color-link);
border-bottom-color: var(--color-link);
}
}
}
}
/* Blame table — repos/file-blame.njk. */
table.blame {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
background: var(--color-pre-bg);
border-radius: 4px;
overflow: hidden;
td {
vertical-align: top;
padding: 0.1rem 0.5rem;
border-bottom: 1px solid var(--color-box-bg);
}
.blame-meta {
white-space: nowrap;
color: var(--color-link);
width: 1%;
}
.blame-line-no {
white-space: nowrap;
opacity: 0.5;
text-align: right;
width: 1%;
}
.blame-content pre {
margin: 0;
white-space: pre-wrap;
}
}
/* diff2html ships its own light/dark token sets as plain CSS custom
properties on :root (see node_modules/diff2html/bundles/css) but only
applies the dark set under a `.d2h-dark-color-scheme` class we never
add. Remapping the base tokens it actually reads to their `--d2h-dark-*`
counterparts under our own [data-theme="dark"] gets the same result
without needing that class or a second copy of every diff2html rule.
This re-themes a third-party library's own classes rather than styling
one of our own, so it's left flat/global rather than nested. */
:root[data-theme="dark"] {
--d2h-bg-color: var(--d2h-dark-bg-color);
--d2h-border-color: var(--d2h-dark-border-color);
--d2h-dim-color: var(--d2h-dark-dim-color);
--d2h-line-border-color: var(--d2h-dark-line-border-color);
--d2h-file-header-bg-color: var(--d2h-dark-file-header-bg-color);
--d2h-file-header-border-color: var(--d2h-dark-file-header-border-color);
--d2h-empty-placeholder-bg-color: var(--d2h-dark-empty-placeholder-bg-color);
--d2h-empty-placeholder-border-color: var(--d2h-dark-empty-placeholder-border-color);
--d2h-selected-color: var(--d2h-dark-selected-color);
--d2h-ins-bg-color: var(--d2h-dark-ins-bg-color);
--d2h-ins-border-color: var(--d2h-dark-ins-border-color);
--d2h-ins-highlight-bg-color: var(--d2h-dark-ins-highlight-bg-color);
--d2h-ins-label-color: var(--d2h-dark-ins-label-color);
--d2h-del-bg-color: var(--d2h-dark-del-bg-color);
--d2h-del-border-color: var(--d2h-dark-del-border-color);
--d2h-del-highlight-bg-color: var(--d2h-dark-del-highlight-bg-color);
--d2h-del-label-color: var(--d2h-dark-del-label-color);
--d2h-change-del-color: var(--d2h-dark-change-del-color);
--d2h-change-ins-color: var(--d2h-dark-change-ins-color);
--d2h-info-bg-color: var(--d2h-dark-info-bg-color);
--d2h-info-border-color: var(--d2h-dark-info-border-color);
--d2h-change-label-color: var(--d2h-dark-change-label-color);
--d2h-moved-label-color: var(--d2h-dark-moved-label-color);
}
.d2h-wrapper {
color: var(--color-text);
}