Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,9 @@ For complex logic, it is recommended to extract it into functions under the `src

### Reuse shared page controls

Use `components/common/*/*.css` for sidebars, search fields, selects, and compact buttons, and
`components/common/dropdown/*` for dropdown variants. `templates/base/head_navbar.tmpl` loads the shared styles and
controllers for all pages. Keep page layout rules in the page stylesheet and pass the page's existing routes,
Use `components/common/*/*.css` for sidebars, search fields, selects, counters, switches, empty states, and compact
buttons, and `components/common/dropdown/*` for dropdown variants. `templates/base/head_navbar.tmpl` loads the shared
styles and controllers for all pages. Keep page layout rules in the page stylesheet and pass the page's existing routes,
permissions, and form values into the shared controls.

- **Sidebar:** add `theme-sidebar-layout` and a unique `data-theme-sidebar` storage key to the page, then use
Expand Down Expand Up @@ -234,6 +234,15 @@ permissions, and form values into the shared controls.
a page stylesheet, with `theme-control-wide` for the 16px padding GitHub uses on profile pages or
`theme-control-small` for 28px controls. Use `theme-icon-button` only for icon-only controls. Keep native `primary`,
`secondary`, and permission checks on actions, and leave page stylesheets to layout such as `flex` and `order`.
- **Counter:** add `theme-counter` to a count pill next to a tab, heading, or filter, including ones a script creates.
Keep only layout (`margin`, `flex`) under the page's own class.
- **Segmented switch:** use `<nav class="theme-switch">` with links and `active` on the current one, as on the releases
and tags pages.
- **Empty state:** use `theme-empty` for a bordered "nothing here" box holding an icon, an `h2` or `h3`, and an optional
paragraph. `templates/user/profile_empty.tmpl` renders one from `Icon`, `Title`, and `Description`. Short inline
messages inside a list or menu stay plain text.
- **Label:** use Forgejo's native `ui basic label` for outline labels such as visibility or package type; do not build a
page-specific pill.
- **Select:** wrap a native `<select class="theme-select">` in `<span class="theme-select-wrap">`. The options stay in
the page template. See `templates/user/profile_packages.tmpl`.

Expand Down
13 changes: 13 additions & 0 deletions components/common/counter/counter.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.theme-counter {
display: inline-block;
min-width: 20px;
padding: 0 6px;
border: 1px solid transparent;
border-radius: 2em;
background: var(--color-label-bg);
color: var(--color-label-text);
font-size: 12px;
font-weight: 500;
line-height: 18px;
text-align: center;
}
29 changes: 29 additions & 0 deletions components/common/counter/counter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { readFileSync, readdirSync } from "node:fs";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { expect, it } from "vitest";

const root = fileURLToPath(new URL("../../..", import.meta.url));
const read = (file: string) => readFileSync(join(root, file), "utf8");
const files = (directory: string, extension: string) =>
readdirSync(join(root, directory), { recursive: true })
.map(file => `${directory}/${String(file)}`)
.filter(file => file.endsWith(extension));

it("renders every count pill through the shared counter", () => {
const pills = ["profile-nav-count", "github-release-asset-count", "diff-filter-count", "issue-state-count"];
const sources = [...files("templates", ".tmpl"), ...files("public/assets/js", ".js")].map(read).join("\n");
for (const pill of pills) {
const uses = sources.match(new RegExp(`"[^"]*\\b${pill}\\b[^"]*"`, "g")) ?? [];
expect(uses.length, `no use of ${pill}`).toBeGreaterThan(0);
for (const use of uses) expect(use).toContain("theme-counter");
}
});

it("keeps one segmented switch and one boxed empty state", () => {
const templates = files("templates", ".tmpl").map(read).join("\n");
expect(templates).not.toMatch(/github-(release|tags)-switch/);
expect(templates.match(/class="theme-switch"/g)).toHaveLength(2);
for (const box of templates.match(/class="[^"]*\b(profile-empty|github-release-empty)\b[^"]*"/g) ?? [])
expect(box).toContain("theme-empty");
});
40 changes: 40 additions & 0 deletions components/common/empty/empty.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.theme-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
min-height: 220px;
padding: 40px 24px;
border: 1px solid var(--color-secondary);
border-radius: var(--border-radius);
text-align: center;
overflow-wrap: anywhere;
}

.theme-empty > .svg {
color: var(--color-text-light-1);
}

.theme-empty h2,
.theme-empty h3 {
margin: 0;
font-size: 20px;
font-weight: 600;
line-height: 28px;
}

.theme-empty p {
max-width: 520px;
margin: 0;
color: var(--color-text-light-1);
font-size: 14px;
line-height: 21px;
}

@media (max-width: 767px) {
.theme-empty {
min-height: 180px;
padding: 32px 16px;
}
}
32 changes: 32 additions & 0 deletions components/common/switch/switch.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.theme-switch {
display: flex;
}

.theme-switch > a {
padding: 5px 16px;
border: 1px solid var(--color-light-border);
color: var(--color-text);
font-weight: 500;
line-height: 20px;
text-decoration: none;
}

.theme-switch > a:first-child {
border-radius: var(--border-radius) 0 0 var(--border-radius);
}

.theme-switch > a:last-child {
margin-left: -1px;
border-radius: 0 var(--border-radius) var(--border-radius) 0;
}

.theme-switch > a:only-child {
margin-left: 0;
border-radius: var(--border-radius);
}

.theme-switch > a.active {
border-color: var(--github-bgColor-accent-emphasis);
background: var(--github-bgColor-accent-emphasis);
color: var(--github-button-primary-fgColor-rest);
}
2 changes: 1 addition & 1 deletion public/assets/js/diff-file-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
checkbox.value = ext;
checkbox.checked = !excludedExtensions.has(ext);
const badge = document.createElement("span");
badge.className = "diff-filter-count";
badge.className = "theme-counter diff-filter-count";
badge.textContent = count;
label.append(checkbox, document.createTextNode(ext), badge);
options.append(label);
Expand Down
2 changes: 1 addition & 1 deletion public/assets/js/issue-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
if (!match) continue;
text.textContent = match[2];
const badge = document.createElement("span");
badge.className = "issue-state-count";
badge.className = "theme-counter issue-state-count";
badge.textContent = match[1].trim();
anchor.append(badge);
}
Expand Down
4 changes: 0 additions & 4 deletions styles/components/diff_controls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,6 @@ export default css`
}
.diff-filter-count {
margin-left: auto;
border-radius: 20px;
background: ${themeVars.color.hover.self};
padding: 0 6px;
font-size: 12px;
}
.diff-tree-options .theme-menu-popover {
min-width: 192px;
Expand Down
7 changes: 0 additions & 7 deletions styles/components/issues/issue_overview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,6 @@ export default css`
& .list-header-author {
order: -1;
}
& .issue-state-count {
border-radius: 20px;
padding: 0 6px;
background: ${themeVars.color.hover.self};
font-size: 12px;
line-height: 18px;
}
&-right > .ui.menu {
gap: 4px;
}
Expand Down
66 changes: 0 additions & 66 deletions styles/components/repo/release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,32 +191,6 @@ const githubReleasePage = css`
margin: 0 0 32px;
padding: 24px 0;
}
.github-release-switch {
display: flex;
gap: 0;
}
.github-release-switch a {
border: 1px solid ${themeVars.color.light.border};
color: ${themeVars.color.text.self};
padding: 7px 16px;
text-decoration: none;
}
.github-release-switch a:first-child {
border-radius: 6px 0 0 6px;
}
.github-release-switch a:last-child {
border-radius: 0 6px 6px 0;
margin-left: -1px;
}
.github-release-switch a:only-child {
border-radius: 6px;
margin-left: 0;
}
.github-release-switch a.active {
background: ${themeVars.github.bgColor.accent.emphasis};
border-color: ${themeVars.github.bgColor.accent.emphasis};
color: ${themeVars.github.button.primary.fgColor.rest};
}
.github-release-actions {
align-items: center;
display: flex;
Expand Down Expand Up @@ -500,11 +474,6 @@ const releaseRefinements = css`
.github-release-toolbar {
margin-bottom: 48px;
}
.github-release-switch a {
padding: 5px 16px;
font-weight: 500;
line-height: 20px;
}
.github-release-search {
width: 248px;
flex: 0 0 248px;
Expand Down Expand Up @@ -644,12 +613,6 @@ const releaseRefinements = css`
margin-top: 0;
}
.github-release-asset-count {
display: inline-block;
border-radius: 24px;
background: var(--color-hover);
font-size: 12px;
line-height: 20px;
padding: 0 6px;
vertical-align: middle;
margin-left: 4px;
}
Expand Down Expand Up @@ -690,10 +653,7 @@ const releaseRefinements = css`
}
}
.github-release-empty {
text-align: center;
padding: 64px 24px;
border: 1px solid var(--color-light-border);
border-radius: 6px;
}
@media (max-width: 767.98px) {
> .ui.container {
Expand Down Expand Up @@ -820,32 +780,6 @@ const githubTagPage = css`
margin-bottom: 16px;
padding: 24px 0;
}
.github-tags-switch {
display: flex;
}
.github-tags-switch a {
border: 1px solid var(--color-light-border);
color: var(--color-text);
line-height: 20px;
padding: 5px 16px;
text-decoration: none;
}
.github-tags-switch a:first-child {
border-radius: 6px 0 0 6px;
}
.github-tags-switch a:last-child {
border-radius: 0 6px 6px 0;
margin-left: -1px;
}
.github-tags-switch a:only-child {
border-radius: 6px;
margin-left: 0;
}
.github-tags-switch a.active {
background: var(--github-bgColor-accent-emphasis);
border-color: var(--github-bgColor-accent-emphasis);
color: var(--github-button-primary-fgColor-rest);
}
.github-tags-search {
width: 248px;
}
Expand Down
17 changes: 1 addition & 16 deletions styles/pages/organization.css
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ body:has(.organization-global-header) .page-content:is(.organization, .packages)
padding: 5px 8px;
}

.github-org .profile-empty,
.organization .org-empty,
.organization .org-package-list > .empty {
display: flex;
flex-direction: column;
Expand All @@ -123,21 +121,8 @@ body:has(.organization-global-header) .page-content:is(.organization, .packages)
text-align: center;
}

.github-org .profile-empty {
border: 0;
}

.github-org .profile-empty h2,
.organization .org-empty h2 {
margin: 0;
font-size: 20px;
}

.organization .org-empty p,
.github-org .profile-empty > .svg,
.organization .org-empty > .svg {
.github-org .profile-empty > .svg {
margin: 0;
color: var(--color-text-light-1);
}

.github-org .org-overview-sidebar > .ui.header {
Expand Down
49 changes: 0 additions & 49 deletions styles/pages/profile-subpages.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,6 @@
line-height: 28px;
}

:is(.github-profile, .github-org) .profile-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
min-height: 220px;
padding: 40px 24px;
border: 1px solid var(--color-secondary);
border-radius: var(--border-radius);
text-align: center;
overflow-wrap: anywhere;
}

:is(.github-profile, .github-org) .profile-empty > .svg {
color: var(--color-text-light-1);
}

:is(.github-profile, .github-org) .profile-empty h2,
:is(.github-profile, .github-org) .profile-empty h3 {
margin: 0;
font-size: 20px;
font-weight: 600;
line-height: 28px;
}

:is(.github-profile, .github-org) .profile-empty p {
max-width: 520px;
margin: 0;
color: var(--color-text-light-1);
font-size: 14px;
line-height: 21px;
}

:is(.github-profile, .github-org) .profile-activity #visibility-hint {
margin: 0 0 24px;
padding: 0;
Expand Down Expand Up @@ -348,16 +314,6 @@
line-height: 24px;
}

:is(.github-profile, .github-org) .profile-type-label {
padding: 0 6px;
border: 1px solid var(--color-secondary);
border-radius: 2em;
color: var(--color-text-light-1);
font-size: 12px;
font-weight: 400;
line-height: 18px;
}

:is(.github-profile, .github-org) .profile-packages .flex-item-body {
display: block;
margin-top: 4px;
Expand Down Expand Up @@ -472,11 +428,6 @@
gap: 4px;
}

:is(.github-profile, .github-org) .profile-empty {
min-height: 180px;
padding: 32px 16px;
}

:is(.github-profile, .github-org) .profile-search-controls {
flex-wrap: wrap;
}
Expand Down
Loading
Loading