vextoly/gitman
Fork of patx/gitman.
Redesign UI with flat black design system and drop CDN assets
Commit ac1dc2e · niklas · 2026-11-03T19:52:16+01:00
Comments
No comments yet.
Diff
diff --git a/app.py b/app.py
index 0531e79..2c35a7c 100644
--- a/app.py
+++ b/app.py
@@ -241,8 +241,8 @@ CSP_HEADER = (
"form-action 'self'; "
"object-src 'none'; "
"img-src 'self' data: http: https:; "
- "style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; "
- "script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com"
+ "style-src 'self' 'unsafe-inline'; "
+ "script-src 'self' 'unsafe-inline'"
)
REPO_INDEX_THREADS = set()
REPO_INDEX_LOCK = threading.Lock()
diff --git a/static/git.svg b/static/git.svg
index 0979cdb..d7bf780 100644
--- a/static/git.svg
+++ b/static/git.svg
@@ -1,3 +1,6 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-git" viewBox="0 0 16 16">
- <path d="M15.698 7.287 8.712.302a1.03 1.03 0 0 0-1.457 0l-1.45 1.45 1.84 1.84a1.223 1.223 0 0 1 1.55 1.56l1.773 1.774a1.224 1.224 0 0 1 1.267 2.025 1.226 1.226 0 0 1-2.002-1.334L8.58 5.963v4.353a1.226 1.226 0 1 1-1.008-.036V5.887a1.226 1.226 0 0 1-.666-1.608L5.093 2.465l-4.79 4.79a1.03 1.03 0 0 0 0 1.457l6.986 6.986a1.03 1.03 0 0 0 1.457 0l6.953-6.953a1.03 1.03 0 0 0 0-1.457"/>
-</svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
+ <circle cx="32" cy="32" r="29" fill="#000000" stroke="#ffffff" stroke-width="6"/>
+ <g transform="translate(14.4 14.4) scale(2.2)" fill="#ffffff">
+ <path d="M15.698 7.287 8.712.302a1.03 1.03 0 0 0-1.457 0l-1.45 1.45 1.84 1.84a1.223 1.223 0 0 1 1.55 1.56l1.773 1.774a1.224 1.224 0 0 1 1.267 2.025 1.226 1.226 0 0 1-2.002-1.334L8.58 5.963v4.353a1.226 1.226 0 1 1-1.008-.036V5.887a1.226 1.226 0 0 1-.666-1.608L5.093 2.465l-4.79 4.79a1.03 1.03 0 0 0 0 1.457l6.986 6.986a1.03 1.03 0 0 0 1.457 0l6.953-6.953a1.03 1.03 0 0 0 0-1.457"/>
+ </g>
+</svg>
diff --git a/static/styles.css b/static/styles.css
index 7c679c7..72812f8 100644
--- a/static/styles.css
+++ b/static/styles.css
@@ -7,213 +7,344 @@ html {
}
:root {
- color-scheme: light;
- --page-bg: #fff;
- --text: #111;
- --link: #0645ad;
- --surface: #fff;
- --soft-surface: #fafafa;
- --button-surface: #eee;
- --border: #ddd;
- --soft-border: #eee;
- --strong-border: #999;
- --control-border: #aaa;
- --menu-border: #bbb;
- --muted: #666;
- --detail: #333;
- --danger: #900;
- --notice: #060;
- --ref-hover: #eef4fb;
- --shadow: rgba(0, 0, 0, .12);
- --app-font: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- color-scheme: dark;
- --page-bg: #101214;
- --text: #e8e8e3;
- --link: #7fb4ff;
- --surface: #15191d;
- --soft-surface: #1b2025;
- --button-surface: #242a30;
- --border: #343a40;
- --soft-border: #2a3036;
- --strong-border: #6b727a;
- --control-border: #6b727a;
- --menu-border: #464d55;
- --muted: #a3a9b0;
- --detail: #d6d8d5;
- --danger: #ff8b8b;
- --notice: #7fd18b;
- --ref-hover: #233246;
- --shadow: rgba(0, 0, 0, .45);
- }
+ color-scheme: dark;
+ --page-bg: #000000;
+ --ink: #ffffff;
+ --text: #b3b3b3;
+ --muted: #999999;
+ --chip: #333333;
+ --press: #e2e2e2;
+ --surface: #161616;
+ --surface-border: #262626;
+ --code-bg: #1f1f1f;
+ --app-font: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
+ --code-font: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}
body {
- max-width: 960px;
- margin: 2rem auto;
- padding: 0 1rem 3rem;
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ min-height: 100svh;
+ margin: 0;
color: var(--text);
background: var(--page-bg);
- font: 16px/1.6 var(--app-font);
+ font: 1rem/1.5 var(--app-font);
+}
+
+main.page {
+ flex: 1 1 auto;
+ width: 100%;
+ max-width: 60rem;
+ margin-inline: auto;
+ padding: clamp(3rem, 9vh, 5.5rem) 24px clamp(3rem, 8vh, 5rem);
+}
+
+h1, h2, h3, h4 {
+ margin: 0 0 .75rem;
+ color: var(--ink);
+ font-weight: 700;
+}
+h1 { font-size: clamp(2rem, 6vw, 2.5rem); line-height: 1.2; }
+h2 { font-size: 1.25rem; line-height: 1.4; }
+h3 { font-size: 1.05rem; line-height: 1.4; }
+
+p, ul, ol, dl { margin: 0 0 .75rem; }
+small { font-size: .875rem; }
+
+a { color: inherit; text-decoration: none; }
+a:hover { color: var(--ink); text-decoration: underline; }
+
+:is(a, button, input, select, textarea):focus-visible {
+ outline: 2px solid var(--ink);
+ outline-offset: 2px;
}
-a { color: var(--link); text-decoration: underline; }
-h1, h2, p, pre, ul, table, form { margin-top: 0; }
-pre, code, input, textarea, select, button { font: inherit; }
-pre, .clone-box code { overflow-x: auto; padding: .6rem; border: 1px solid var(--border); background: var(--soft-surface); font: 14px/1.55 var(--app-font); }
-pre code.hljs { padding: 0; background: transparent; }
+form { display: grid; gap: .9rem; }
+label { display: grid; gap: .35rem; align-content: start; }
+
+input, textarea, select {
+ width: 100%;
+ padding: 12px 14px;
+ color: var(--ink);
+ background: var(--surface);
+ border: 1px solid var(--surface-border);
+ border-radius: 8px;
+ font: inherit;
+}
+input::placeholder { color: var(--muted); }
+textarea { resize: vertical; }
+
+button, .button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+ min-height: 48px;
+ padding: 12px 18px;
+ border: 0;
+ border-radius: 999px;
+ background: var(--chip);
+ color: var(--ink);
+ font: inherit;
+ font-weight: 500;
+ text-decoration: none;
+ cursor: pointer;
+}
+button:hover, .button:hover {
+ background: var(--ink);
+ color: var(--page-bg);
+ text-decoration: none;
+}
+button:active, .button:active {
+ background: var(--press);
+ color: var(--page-bg);
+}
+.button.small { min-height: 44px; padding: 8px 14px; }
+.button.danger { background: var(--ink); color: var(--page-bg); }
+.button.danger:hover { background: var(--press); }
+.button.danger:active { background: var(--chip); color: var(--ink); }
+
+form button[type="submit"] { justify-self: start; }
+
+@media (prefers-reduced-motion: no-preference) {
+ button, .button, .nav-icons, .repo-search-link, .ref-picker-option, .ref-picker-toggle, .mention-option {
+ transition: background-color 150ms ease, color 150ms ease;
+ }
+}
+
+code { font-family: var(--code-font); font-size: .9em; background: var(--code-bg); border-radius: 3px; padding: .12em .35em; }
+pre {
+ margin: 0 0 .75rem;
+ overflow-x: auto;
+ padding: .75rem 1rem;
+ color: var(--text);
+ background: var(--surface);
+ border: 1px solid var(--surface-border);
+ border-radius: 8px;
+ font: .875rem/1.55 var(--code-font);
+}
+pre code { padding: 0; background: transparent; border-radius: 0; font-size: inherit; }
+.clone-box code {
+ display: block;
+ overflow-x: auto;
+ padding: .75rem 1rem;
+ background: var(--surface);
+ border: 1px solid var(--surface-border);
+ border-radius: 8px;
+ font: .875rem/1.55 var(--code-font);
+}
+.diff { max-height: 70vh; overflow: auto; }
+
.copyable-code {
position: relative;
display: flex;
align-items: stretch;
- border: 1px solid var(--border);
- background: var(--soft-surface);
+ background: var(--surface);
+ border: 1px solid var(--surface-border);
+ border-radius: 8px;
}
.copyable-code pre {
margin: 0;
border: 0;
background: transparent;
+ border-radius: 0;
}
.copyable-code pre.code {
flex: 1 1 auto;
min-width: 0;
- padding: 2.1rem .6rem .6rem;
+ padding: 2.4rem 1rem .75rem;
}
.copyable-code pre.line-numbers {
flex: 0 0 auto;
overflow: hidden;
- padding: 2.1rem .55rem .6rem .6rem;
+ padding: 2.4rem .75rem .75rem 1rem;
color: var(--muted);
text-align: right;
user-select: none;
- border-right: 1px solid var(--border);
+ border-right: 1px solid var(--surface-border);
}
.copy-button {
position: absolute;
- top: .45rem;
- right: .45rem;
+ top: .55rem;
+ right: .55rem;
display: inline-flex;
align-items: center;
justify-content: center;
- width: 1.9rem;
- height: 1.9rem;
+ width: 2rem;
+ height: 2rem;
+ min-height: 0;
padding: 0;
- border-radius: .35rem;
- background: var(--surface);
- box-shadow: 0 1px 2px var(--shadow);
-}
-.copy-button svg {
- width: 1rem;
- height: 1rem;
- fill: currentColor;
-}
-.copy-button.is-copied {
- color: var(--notice);
- border-color: var(--notice);
-}
-.copy-button.copy-error {
- color: var(--danger);
- border-color: var(--danger);
-}
-input, textarea, select { width: 100%; padding: .4rem; color: var(--text); border: 1px solid var(--control-border); background: var(--surface); }
+ border-radius: 999px;
+ background: var(--chip);
+ color: var(--ink);
+}
+.copy-button:hover { background: var(--ink); color: var(--page-bg); }
+.copy-button svg { width: 1rem; height: 1rem; fill: currentColor; }
+.copy-button.is-copied { background: var(--ink); color: var(--page-bg); }
+.copy-button.copy-error { outline: 2px solid var(--ink); outline-offset: 1px; }
+
table { width: 100%; border-collapse: collapse; }
-th, td { padding: .35rem .5rem; border: 1px solid var(--border); text-align: left; }
+th, td { padding: .4rem .6rem; border: 1px solid var(--surface-border); text-align: left; }
+th { color: var(--ink); font-weight: 700; }
-button, .button {
- display: inline-block;
- padding: .3rem .55rem;
- color: var(--text);
- background: var(--button-surface);
- border: 1px solid var(--strong-border);
- text-decoration: none;
- cursor: pointer;
-}
-.pagination {
+.site-header {
display: flex;
- flex-wrap: wrap;
- gap: .6rem;
+ justify-content: space-between;
align-items: center;
- margin-top: 1rem;
-}
-.pagination-current, .pagination-disabled { color: var(--muted); }
-.pagination-disabled { padding: .3rem 0; }
-
-.sr-only {
- position: absolute;
- width: 1px;
- height: 1px;
- padding: 0;
- margin: -1px;
- overflow: hidden;
- clip: rect(0, 0, 0, 0);
- white-space: nowrap;
- border: 0;
-}
-
-.site-header, .repo-header, .profile-header, .panel, .auth-card {
- margin-bottom: 1.5rem;
- padding-bottom: 1rem;
- border-bottom: 1px solid var(--border);
-}
-
-.site-header, .nav, .repo-tabs, .filters, .tabs, .panel-heading, .hero-actions, .breadcrumb, .repo-actions, .ref-actions {
- display: flex;
- gap: .75rem;
- align-items: baseline;
+ gap: 1rem;
flex-wrap: wrap;
+ width: 100%;
+ max-width: 60rem;
+ margin-inline: auto;
+ padding: 24px;
}
-
-.site-header, .panel-heading, .repo-header { justify-content: space-between; }
-.repo-title-row { display: flex; gap: .75rem; align-items: baseline; flex-wrap: wrap; }
-.repo-title-row h1 { margin-bottom: 0; }
.brand {
display: inline-flex;
align-items: center;
gap: 1rem;
- color: var(--text);
- font-weight: 700;
+ color: var(--ink);
line-height: 1;
text-decoration: none;
}
+.brand:hover { color: var(--ink); text-decoration: none; }
.brand svg {
flex: 0 0 auto;
transform: scale(2);
transform-origin: center;
}
-.site-header, .nav { align-items: center; }
+.nav, .repo-tabs, .filters, .tabs, .panel-heading, .hero-actions, .breadcrumb {
+ display: flex;
+ gap: .75rem;
+ align-items: baseline;
+ flex-wrap: wrap;
+}
+.nav { align-items: center; }
+.panel-heading, .repo-header { justify-content: space-between; }
+.panel-heading { margin-bottom: 1.25rem; }
+.panel-heading h2 { margin: 0; }
.nav-icons {
display: inline-flex;
align-items: center;
justify-content: center;
- width: 24px;
- height: 24px;
+ width: 40px;
+ height: 40px;
+ color: var(--text);
+}
+.nav-icons:hover { color: var(--ink); text-decoration: none; }
+.nav-icons svg { display: block; width: 20px; height: 20px; flex-shrink: 0; }
+.nav form, .repo-tabs form, .inline-form { display: inline; }
+
+.colophon {
+ display: flex;
+ justify-content: space-between;
+ gap: 1rem;
+ flex-wrap: wrap;
+ width: 100%;
+ max-width: 60rem;
+ margin-inline: auto;
+ padding: 1.5rem 24px 2.5rem;
+ color: var(--muted);
+ font-size: .875rem;
+}
+.colophon p { margin: 0; }
+.colophon a { color: var(--muted); }
+.colophon a:hover { color: var(--ink); text-decoration: underline; }
+
+.repo-header, .profile-header { margin-bottom: 2.5rem; }
+.repo-header { display: flex; align-items: flex-end; gap: 2rem; flex-wrap: wrap; }
+.panel { margin-bottom: 2.5rem; }
+.panel > :last-child, .repo-header > :last-child, .auth-card > :last-child { margin-bottom: 0; }
+.auth-card { max-width: 32rem; margin-bottom: 2.5rem; }
+.auth-card.wide { max-width: 36rem; }
+.repo-title-row { display: flex; gap: 1rem; align-items: baseline; flex-wrap: wrap; }
+.repo-title-row h1 { margin-bottom: 0; }
+
+.repo-tabs .repo-tab { padding: .25rem 0; }
+.repo-tab.active, .filters .active, .tabs .active {
+ color: var(--ink);
font-weight: 700;
- line-height: 1;
text-decoration: none;
- color: var(--muted);
}
-.nav-icons svg { display: block; width: 24px; height: 24px; flex-shrink: 0; }
-.nav form, .repo-tabs form, .inline-form { display: inline; }
-.link-button { padding: 0; color: var(--link); background: none; border: 0; text-decoration: underline; }
-.repo-tabs .repo-tab { padding-bottom: .2rem; border-bottom: 2px solid transparent; }
-.repo-tabs .repo-tab.active { color: var(--text); font-weight: 700; text-decoration: none; }
-.repo-search {
- position: relative;
- width: 18rem;
- max-width: 100%;
- align-self: flex-start;
+
+.link-button, .button-link {
+ display: inline;
+ min-height: 0;
+ padding: 0;
+ border: 0;
+ border-radius: 0;
+ background: none;
+ color: var(--text);
+ font: inherit;
+ text-decoration: none;
+ cursor: pointer;
}
-.repo-search-input {
- width: 350px;
- margin-bottom: 10px;
+.link-button:hover, .button-link:hover {
+ background: none;
+ color: var(--ink);
+ text-decoration: underline;
}
-.repo-search-form {
- display: block;
+
+.eyebrow, .muted, .empty, .issue-list span { color: var(--muted); }
+.eyebrow { font-size: .875rem; }
+
+.repo-list, .clean-list, .commit-list, .file-list, .issue-list, .comment-list, .activity-feed {
+ display: grid;
+ gap: .75rem;
+ padding-left: 0;
margin: 0;
+ list-style: none;
+}
+.commit-list li, .file-list li { display: grid; grid-template-columns: 8rem 1fr; gap: 1rem; }
+.commit-list li > code, .file-list li > code { justify-self: start; }
+
+.meta-list { display: grid; grid-template-columns: 8rem 1fr; gap: .35rem 1rem; }
+.meta-list dt { color: var(--muted); }
+.meta-list dd { margin: 0; overflow-x: auto; }
+
+.activity-feed { gap: 1rem; }
+.activity-feed li { padding-bottom: 1rem; border-bottom: 1px solid var(--surface-border); }
+.activity-feed li:last-child { border-bottom: 0; }
+.activity-title, .activity-detail { margin-bottom: .25rem; }
+.activity-detail { color: var(--text); }
+
+.comment { padding-bottom: 1rem; border-bottom: 1px solid var(--surface-border); min-width: 0; }
+.comment:last-child { border-bottom: 0; padding-bottom: 0; }
+.comment-meta {
+ display: flex;
+ gap: .5rem;
+ align-items: baseline;
+ margin-bottom: .35rem;
+}
+.comment-body {
+ line-height: 1.6;
+ overflow-wrap: anywhere;
+}
+.comment-body > :last-child { margin-bottom: 0; }
+.comment p, .activity-title, .activity-detail {
+ font-size: 1rem;
+ line-height: 1.6;
+ overflow-wrap: anywhere;
+}
+.comment small, .activity-feed .muted { font-size: .875rem; }
+
+.markdown-body { overflow-x: auto; }
+.markdown-body img { max-width: 100%; }
+.markdown-body h1 { font-size: 1.5rem; margin: 1.5rem 0 .75rem; }
+.markdown-body h2 { font-size: 1.25rem; margin: 1.5rem 0 .75rem; }
+.markdown-body h3 { font-size: 1.05rem; margin: 1.25rem 0 .6rem; }
+.markdown-body h1:first-child, .markdown-body h2:first-child, .markdown-body h3:first-child { margin-top: 0; }
+.markdown-body blockquote {
+ margin: 0 0 .75rem;
+ padding: .25rem 0 .25rem 1rem;
+ border-left: 2px solid var(--chip);
+ color: var(--muted);
}
+.markdown-body a { text-decoration: underline; }
+
+.repo-search { position: relative; width: 18rem; max-width: 100%; align-self: flex-start; }
+.repo-search-input { width: 350px; margin-bottom: 10px; }
+.repo-search-form { display: block; margin: 0; }
.panel-heading > .repo-search { margin-left: auto; }
.hero-actions .repo-search-menu { left: 0; right: auto; }
.repo-search-menu {
@@ -229,25 +360,25 @@ button, .button {
max-height: 20rem;
overflow-y: auto;
padding: .45rem;
- border: 1px solid var(--menu-border);
+ border: 1px solid var(--surface-border);
+ border-radius: 8px;
background: var(--surface);
- box-shadow: 0 .4rem 1.2rem var(--shadow);
-}
-.repo-search-result {
- display: block;
}
+.repo-search-result { display: block; }
.repo-search-link {
display: flex;
justify-content: space-between;
gap: .75rem;
align-items: baseline;
width: 100%;
- padding: .4rem .5rem;
- color: var(--text);
+ padding: .5rem .6rem;
+ border-radius: 6px;
+ color: var(--ink);
text-decoration: none;
}
.repo-search-link:hover, .repo-search-link:focus {
- background: var(--ref-hover);
+ background: var(--ink);
+ color: var(--page-bg);
text-decoration: none;
}
.repo-search-link strong {
@@ -256,13 +387,27 @@ button, .button {
text-overflow: ellipsis;
white-space: nowrap;
}
-.repo-search-link small {
- flex: 0 0 auto;
- white-space: nowrap;
-}
+.repo-search-link small { flex: 0 0 auto; white-space: nowrap; }
.repo-search-link small, .repo-search-empty { color: var(--muted); }
+.repo-search-link:hover small, .repo-search-link:focus small { color: inherit; }
.repo-search-empty { margin: 0; padding: .3rem .4rem; }
+
.ref-picker { position: relative; display: inline-block; }
+.ref-picker-toggle {
+ display: inline-flex;
+ gap: .25rem;
+ align-items: center;
+ min-height: 0;
+ padding: .3rem .9rem;
+ border: 0;
+ border-radius: 999px;
+ background: var(--chip);
+ color: var(--ink);
+ font: inherit;
+ font-size: .9375rem;
+ cursor: pointer;
+}
+.ref-picker-toggle:hover { background: var(--ink); color: var(--page-bg); }
.ref-picker-menu {
position: absolute;
left: 0;
@@ -271,26 +416,29 @@ button, .button {
width: 20rem;
max-width: calc(100vw - 2rem);
padding: .5rem;
- border: 1px solid var(--menu-border);
+ border: 1px solid var(--surface-border);
+ border-radius: 8px;
background: var(--surface);
- box-shadow: 0 .4rem 1.2rem var(--shadow);
}
.ref-picker-search { margin-bottom: .4rem; }
-.ref-picker-options { display: grid; gap: .1rem; max-height: 18rem; overflow-y: auto; }
+.ref-picker-options { display: grid; gap: .15rem; max-height: 18rem; overflow-y: auto; }
.ref-picker-option {
display: flex;
gap: 1rem;
justify-content: space-between;
- padding: .35rem .45rem;
- color: var(--text);
+ padding: .45rem .6rem;
+ border-radius: 6px;
+ color: var(--ink);
text-decoration: none;
}
-.ref-picker-option:hover, .ref-picker-option:focus, .ref-picker-option.active {
- background: var(--ref-hover);
+.ref-picker-option:hover, .ref-picker-option:focus {
+ background: var(--ink);
+ color: var(--page-bg);
text-decoration: none;
}
.ref-picker-option.active { font-weight: 700; }
.ref-picker-current { color: var(--muted); font-weight: 400; }
+.ref-picker-option:hover .ref-picker-current, .ref-picker-option:focus .ref-picker-current { color: inherit; }
.ref-picker-empty { padding: .35rem .45rem; color: var(--muted); }
.ref-picker-footer {
display: flex;
@@ -298,107 +446,47 @@ button, .button {
flex-wrap: wrap;
margin-top: .5rem;
padding-top: .45rem;
- border-top: 1px solid var(--border);
+ border-top: 1px solid var(--surface-border);
}
-.ref-picker-link.active { color: var(--text); font-weight: 700; text-decoration: none; }
-.filters .active, .tabs .active { color: var(--text); font-weight: 700; text-decoration: none; }
+.ref-picker-link.active { color: var(--ink); font-weight: 700; text-decoration: none; }
+
.list-search {
display: inline-flex;
gap: .4rem;
align-items: center;
flex-wrap: wrap;
}
-.list-search input[type="search"] {
- width: 18rem;
- max-width: 100%;
-}
-.hero, .repo-tabs { margin-bottom: 1.5rem; }
-.eyebrow, .muted, .empty, .nav-user, .repo-card small, .issue-list span, .commit-list span, .file-list span, .clean-list span, .file-kind { color: var(--muted); }
-.repo-list, .stack, form, .clean-list, .commit-list, .file-list, .issue-list { display: grid; gap: .75rem; }
-.repo-card { display: block; padding: .75rem 0; border-bottom: 1px solid var(--soft-border); color: var(--text); }
-.repo-card strong, .repo-card span, .repo-card small, .clone-box code { display: block; }
-.markdown-body { overflow-x: auto; }
-.markdown-body img { max-width: 100%; }
-.markdown-body code {
- padding: .12rem .28rem;
- border-radius: .25rem;
- background: var(--soft-surface);
- font: .9em/1.45 var(--app-font);
-}
-.markdown-body pre code {
- display: block;
- padding: 0;
- background: transparent;
- font: inherit;
-}
-.grid.two { display: grid; grid-template-columns: 1fr 18rem; gap: 2rem; }
-.meta-list { display: grid; grid-template-columns: 8rem 1fr; gap: .35rem 1rem; }
-.meta-list dt { color: var(--muted); }
-.meta-list dd { margin: 0; overflow-x: auto; }
-.activity-feed { display: grid; gap: 1rem; padding-left: 0; list-style: none; }
-.activity-feed li { padding-bottom: 1rem; border-bottom: 1px solid var(--soft-border); }
-.activity-title, .activity-detail { margin-bottom: .25rem; }
-.activity-detail { color: var(--detail); }
-.comment-list { display: grid; gap: 1rem; }
-.comment { padding-bottom: 1rem; border-bottom: 1px solid var(--soft-border); }
-.comment, .activity-feed li { min-width: 0; }
-.comment-meta {
+.list-search input[type="search"] { width: 18rem; max-width: 100%; }
+
+.hero-actions { margin-top: 1.25rem; }
+
+.pagination {
display: flex;
- gap: .5rem;
- align-items: baseline;
- margin-bottom: .35rem;
-}
-.comment-body {
- line-height: 1.6;
- overflow-wrap: anywhere;
-}
-.comment-body > :last-child { margin-bottom: 0; }
-.comment p, .activity-title, .activity-detail {
- font-size: 1rem;
- line-height: 1.6;
- overflow-wrap: anywhere;
-}
-.comment small, .activity-feed .muted {
- font-size: .875rem;
+ flex-wrap: wrap;
+ gap: .6rem;
+ align-items: center;
+ margin-top: 1rem;
}
-.diff { max-height: 70vh; }
-.file-table { width: 100%; border-collapse: collapse; }
-.file-table td { padding: .4rem 0; border-bottom: 1px solid var(--soft-border); }
-.file-kind { width: 4rem; }
-.commit-list, .file-list, .issue-list, .clean-list { padding-left: 0; list-style: none; }
-.commit-list li, .file-list li { display: grid; grid-template-columns: 8rem 1fr; gap: 1rem; }
-.tag-list li { display: grid; grid-template-columns: 1fr 18rem; gap: 1.5rem; padding-bottom: 1rem; border-bottom: 1px solid var(--soft-border); }
-.tag-actions { display: grid; gap: .75rem; align-content: start; }
-.alert { margin-bottom: 1rem; color: var(--danger); }
-.notice { margin-bottom: 1rem; color: var(--notice); }
-.danger-zone { color: var(--danger); }
-.button.danger { color: var(--danger); border-color: var(--danger); }
-.button-link { background: none; border: none; padding: 0; color: var(--link); text-decoration: underline; cursor: pointer; font: inherit; }
-.ref-picker-toggle { display: inline-flex; gap: .25rem; align-items: center; background: none; border: none; padding: 0; color: var(--muted); cursor: pointer; font: inherit; font-size: smaller; }
+.pagination-current, .pagination-disabled { color: var(--muted); }
+.pagination-disabled { padding: .3rem 0; }
-@media (max-width: 760px) {
- body { margin-top: 1rem; }
- .repo-search { width: 100%; }
- .repo-search-input { width: 100%; }
- .repo-search-menu { left: 0; right: auto; width: 100%; }
- .grid.two, .commit-list li, .tag-list li { grid-template-columns: 1fr; }
-}
+.alert { margin-bottom: 1rem; color: var(--ink); font-weight: 700; }
+.notice { margin-bottom: 1rem; color: var(--text); }
+.danger-zone { margin-top: 1rem; padding-top: 2rem; border-top: 1px solid var(--surface-border); }
+.danger-zone h2 { margin-top: 0; }
.password-dialog {
width: min(28rem, calc(100% - 2rem));
max-height: calc(100dvh - 2rem);
overflow: auto;
padding: 1.5rem;
- border: 1px solid var(--strong-border);
- border-radius: .5rem;
+ border: 1px solid var(--surface-border);
+ border-radius: 8px;
color: var(--text);
background: var(--surface);
- box-shadow: 0 12px 48px var(--shadow);
}
-.password-dialog::backdrop { background: rgb(0 0 0 / .45); }
+.password-dialog::backdrop { background: rgb(0 0 0 / .6); }
.password-dialog h2 { font-size: 1.25rem; margin-bottom: 1rem; }
-.password-dialog label { display: block; margin-bottom: 1rem; }
-.password-dialog input { margin-top: .25rem; }
.password-dialog .muted { font-size: .875rem; }
.password-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: .75rem; }
.password-dialog form { margin-bottom: 0; }
@@ -409,16 +497,48 @@ button, .button {
margin: 0;
}
.mention-option {
- flex-shrink: 0;
+ display: flex;
+ justify-content: space-between;
+ gap: .75rem;
+ align-items: baseline;
+ width: 100%;
+ min-height: 0;
+ padding: .5rem .6rem;
border: 0;
+ border-radius: 6px;
background: transparent;
+ color: var(--ink);
+ font-weight: 400;
text-align: left;
cursor: pointer;
}
-.mention-option.active { background: var(--ref-hover); }
+.mention-option:hover, .mention-option.active {
+ background: var(--ink);
+ color: var(--page-bg);
+}
.mention-option small {
color: var(--muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
+.mention-option:hover small, .mention-option.active small { color: inherit; }
+
+@media (max-width: 760px) {
+ .repo-search, .repo-search-input { width: 100%; }
+ .repo-search-menu { left: 0; right: auto; width: 100%; }
+ .commit-list li, .file-list li { grid-template-columns: 1fr; }
+ .repo-header { align-items: flex-start; }
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
diff --git a/templates/base.tpl b/templates/base.tpl
index fe5774f..7a7f619 100644
--- a/templates/base.tpl
+++ b/templates/base.tpl
@@ -3,21 +3,20 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
- <title>{{title or "GitMan"}}</title>
- <link rel="icon" type="image/svg+xml" sizes="32x32" href="/static/git.svg">
-<link rel="icon" type="image/svg+xml" sizes="16x16" href="/static/git.svg">
-<link rel="apple-touch-icon" sizes="180x180" href="/static/git.svg">
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css">
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css" media="(prefers-color-scheme: dark)">
- <link rel="stylesheet" href="/static/styles.css?v=8">
+ <meta name="color-scheme" content="dark">
+ <meta name="theme-color" content="#000000">
+ <meta name="description" content="GitMan: free Git repository hosting for open source software.">
+ <title>{{(title + " · GitMan") if title else "GitMan"}}</title>
+ <link rel="icon" type="image/svg+xml" href="/static/git.svg">
+ <link rel="stylesheet" href="/static/styles.css?v=9">
</head>
<body>
<header class="site-header">
- <a class="brand" href="/">
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-git" viewBox="0 0 16 16">
+ <a class="brand" href="/" aria-label="GitMan home">
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-git" viewBox="0 0 16 16" aria-hidden="true">
<path d="M15.698 7.287 8.712.302a1.03 1.03 0 0 0-1.457 0l-1.45 1.45 1.84 1.84a1.223 1.223 0 0 1 1.55 1.56l1.773 1.774a1.224 1.224 0 0 1 1.267 2.025 1.226 1.226 0 0 1-2.002-1.334L8.58 5.963v4.353a1.226 1.226 0 1 1-1.008-.036V5.887a1.226 1.226 0 0 1-.666-1.608L5.093 2.465l-4.79 4.79a1.03 1.03 0 0 0 0 1.457l6.986 6.986a1.03 1.03 0 0 0 1.457 0l6.953-6.953a1.03 1.03 0 0 0 0-1.457"/>
</svg>
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person-arms-up" viewBox="0 0 16 16">
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person-arms-up" viewBox="0 0 16 16" aria-hidden="true">
<path d="M8 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3"/>
<path d="m5.93 6.704-.846 8.451a.768.768 0 0 0 1.523.203l.81-4.865a.59.59 0 0 1 1.165 0l.81 4.865a.768.768 0 0 0 1.523-.203l-.845-8.451A1.5 1.5 0 0 1 10.5 5.5L13 2.284a.796.796 0 0 0-1.239-.998L9.634 3.84a.7.7 0 0 1-.33.235c-.23.074-.665.176-1.304.176-.64 0-1.074-.102-1.305-.176a.7.7 0 0 1-.329-.235L4.239 1.286a.796.796 0 0 0-1.24.998l2.5 3.216c.317.316.475.758.43 1.204Z"/>
</svg>
@@ -42,7 +41,7 @@
</a>
% else:
<a href="/login">Log in</a>
- <a class="button small" href="/signup">Sign up</a>
+ <a class="button" href="/signup">Sign up</a>
% end
</nav>
</header>
@@ -56,8 +55,11 @@
% end
{{!base}}
</main>
+ % import time
+ <footer class="colophon">
+ <p>© {{time.strftime("%Y")}} GitMan</p>
+ </footer>
<script src="/static/mentions.js?v=1" defer></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script>
(() => {
const addLineNumbers = (code, rawText) => {
diff --git a/templates/index.tpl b/templates/index.tpl
index c85e177..510c12c 100644
--- a/templates/index.tpl
+++ b/templates/index.tpl
@@ -1,16 +1,16 @@
-% rebase("base.tpl", title="GitMan activity", user=user, error=error, notice=notice)
+% rebase("base.tpl", title="Recent activity", user=user, error=error, notice=notice)
-<section class="panel" style="border-bottom: none;">
+<section class="panel">
<div class="panel-heading">
<div>
<p class ="eyebrow">GitMan.io: Free Git repository hosting for open source software</p>
% if not user:
- <div class="hero-actions" style="margin-bottom:50px;">
+ <div class="hero-actions">
<a class="button" href="/signup">Create an account</a>
- <a class="button secondary" href="/login">Log in</a>
+ <a class="button" href="/login">Log in</a>
</div>
% else:
- <div class="hero-actions" style="margin-bottom:50px;">
+ <div class="hero-actions">
<div class="repo-search" data-repo-search data-repo-search-url="/-/repos/search">
<input
id="repo-search-input"
diff --git a/templates/notifications.tpl b/templates/notifications.tpl
index f5ccf9f..6f6bbf6 100644
--- a/templates/notifications.tpl
+++ b/templates/notifications.tpl
@@ -1,6 +1,6 @@
-% rebase("base.tpl", title="Notifications · GitMan", user=user, error=error, notice=notice)
+% rebase("base.tpl", title="Notifications", user=user, error=error, notice=notice)
-<section class="panel" style="border-bottom: none;">
+<section class="panel">
<div class="panel-heading">
<div>
<h1>Notifications</h1>
diff --git a/tests/test_app.py b/tests/test_app.py
index c4806ef..a9e7191 100644
--- a/tests/test_app.py
+++ b/tests/test_app.py
@@ -513,16 +513,17 @@ def test_security_headers_and_secure_cookie_flags(isolated_app):
assert "Secure" in session_cookie
-def test_styles_support_system_dark_mode(isolated_app):
+def test_styles_apply_permanent_dark_theme(isolated_app):
css = (REPO_ROOT_FOR_IMPORTS / "static" / "styles.css").read_text(encoding="utf-8")
client = WsgiClient(isolated_app.app)
response = client.get("/")
- assert "@media (prefers-color-scheme: dark)" in css
assert "color-scheme: dark" in css
- assert "var(--page-bg)" in css
- assert "github-dark.min.css" in response.text
- assert 'media="(prefers-color-scheme: dark)"' in response.text
+ assert "--page-bg: #000000" in css
+ assert "prefers-color-scheme" not in css
+ assert "prefers-reduced-motion: no-preference" in css
+ assert "cdnjs.cloudflare.com" not in response.text
+ assert 'name="theme-color" content="#000000"' in response.text
def test_styles_keep_comment_and_activity_text_readable_on_mobile(isolated_app):