Skip to content

Latest commit

 

History

History
367 lines (277 loc) · 19.4 KB

CONTRIBUTING.md

File metadata and controls

367 lines (277 loc) · 19.4 KB

Contributing Guide

Directory Structure

Directory Description
src Theme generation framework and utilities
src/core Theme generation framework (CSS vars, syntax highlighting styles)
src/functions Theme utilities (color calculations, etc.)
src/palette Theme palettes (GitHub / Gitea / Catppuccin)
src/types Theme color variable type definitions
src/vite-plugin Theme Vite build plugin
styles Theme-wide overrides in TypeScript, built into every theme stylesheet
styles/common Reusable CSS style fragments
styles/public Base element or cross-page element styles
styles/components Overrides of Forgejo pages by Forgejo's own markup
styles/templates Styles for markup only the theme's older repository templates render
styles/pages Page-only CSS for the theme's templates
components Reusable UI: template partials, stylesheet, browser script and tests
templates Forgejo template overrides (optional)
public Stylesheets and scripts that one page loads from its template
themes Color theme definitions
primer Auto-generated Primer design tokens
scripts Build and utility scripts, including the template pack assembly
dev Local Forgejo preview and its fixtures
.github/assets/screenshots Theme screenshots

ARCHITECTURE.md explains how these parts are built into the theme stylesheets and the template pack.

Contribution Notes

Style contributions are not recommended, as the theme-wide overrides are built into every theme stylesheet and the shared components load on every page, which introduces issues like ordering/override conflicts and broad-reaching style impacts.

Each developer has their own approach, making it difficult for review to catch these issues, greatly increasing maintenance difficulty.

If you have confirmed understanding of Forgejo's markup and the project's approach, please first file an Issue to confirm scope and expected results, then develop and submit a PR.

If you believe you have a better approach, feel free to submit an Issue.

Important

When submitting a PR, please run bun commit

Pull Requests and Release Notes

Submit changes through a pull request targeting dev, or stacked on another pull request; checks run for both. A merged pull request lands in the release draft as * <title> ([#<number>](<url>)), under the category of its label. The draft is written for the people who install the theme, so the title and the label decide what they read.

Titles

  • Use the strict format Area: Description, with a past-tense description of what changed for the reader: Introduced, Changed, Fixed, Removed.
  • Describe the effect, not the implementation: Branches: Fixed the pull request column on phones, not Branches: Changed branch.css.
  • The draft sorts by the exact area, so spell an area exactly as listed below.

Areas: Actions, Auth, Branches, Code, Commits, Dashboard, Editor, Explore, Forks, Forms, Insights, Issues, Labels, Milestones, Navbar, Notifications, Organizations, Packages, Profile, Projects, Pull requests, Releases, Repository, Settings, Teams, Templates, Themes, Wiki.

Reuse an existing area whenever one fits; add a new area only for a page people recognise by that name, and add it to this list in the same pull request. Never use file, component or style names as an area.

Labels

Label Use for Draft section
feature a page, control or theme that is new to users What’s new
change a changed look, template or compatibility Enhancements
bug a fix for a bug that shipped in a release Fixes
dependencies a dependency or build-tool update Dependencies
skip-changelog anything a reader of the release would not notice not listed

Apply exactly one of them; the pull request metadata check enforces it with the title format. A fix for a bug that only existed on dev is skip-changelog. Add major when admins must act on upgrade, for example a new minimum Forgejo version; it raises the major version and does not add a section. suggestion marks an idea that has not been decided on yet.

Release notes and screenshots

The summary and the compatibility section at the top of the draft come from .github/release-notes.md when its front matter names the version the draft resolves to. Update it in the pull request that needs upgrade steps or warnings, instead of editing the draft.

A pull request that changes how a page looks shows it in a Before and after table in its description: the same page, viewport and state, captured in the dev preview.

Development Environment

The theme depends on the Bun runtime. Please ensure Bun is installed in your environment. Bun 1.3.14 or later is recommended.

Please use VSCode for development and install the recommended extensions in the repository.

If you prefer fewer extensions, you must install the vscode-styled-components extension, which is used for rendering and checking CSS template strings in TypeScript.

VSCode 1.102.0 or later is recommended for development, as this version and above support hex color rendering in TypeScript code.

Development Workflow

Install Dependencies

bun install

Set Environment Variables

Create a .env file in the project root. Refer to .env.example for available variables.

Environment variables are used to send compiled themes to a server for quick preview.

Compile Themes

To check changes in a running Forgejo, use the local preview described in README.md. To compile and sync to a remote server instead:

bun dev

Compile all themes:

bun bundle

Format project code:

bun fmt

For PR submission — check and compile all project code with formatting:

bun commit

Upgrading Forgejo

The files in templates are Forgejo's templates with the theme's changes. The forgejo field of package.json pins the Forgejo release they were copied from; the dev preview runs the same release, and a test checks that dev/docker-compose.yml names its image.

bun drift checks out the templates of the pinned release into node_modules/.cache/forgejo (git and network on the first run only) and lists each override by the lines the theme added and removed, the largest first, then the templates Forgejo does not have. bun drift 17.1.0 does the same against another release.

To move to a new release:

  1. Run bun drift <pinned> <new>, for example bun drift 17.0.0 17.1.0. It lists the overridden templates Forgejo changed between the two releases, with Forgejo's diff of each, and the ones it added or removed.
  2. Port each change into the override, and rename or remove an override whose file Forgejo moved or removed.
  3. Set forgejo in package.json and the image in dev/docker-compose.yml to the new release, then check the changed pages in the dev preview.

Development Conventions

src, styles, and themes are the project's main directories.

When referencing across modules, use package exports paths. For example: import { defineTheme } from "@lutinglt/gitea-github-theme/core"

When referencing files within the same module, use relative paths. For example: import { defineTheme } from "./theme"

Color Theme Contributions

Theme file naming format: themeName.ts, used together with theme.config.ts registration.

If a theme has both dark and light variants, the framework automatically generates an auto color theme.

The project accepts custom themes and will include them in published releases, but the project owner does not participate in their maintenance or review.

Please add your author info at the top of the color theme file so Issue reporters can @ mention you.

It is recommended to use defineTheme with the ThemeColor type to define theme colors. The framework will automatically compute and generate all Gitea CSS variables.

Color calculation functions can be imported from @lutinglt/gitea-github-theme/core (scaleColorLight, rgba), or use the color2k library for additional color manipulation.

Example: themes/my-theme.ts

/**
 * @author your-name
 * @description theme description
 */
import { defineTheme, scaleColorLight } from "@lutinglt/gitea-github-theme/core";
import type { ThemeColor, Syntax } from "@lutinglt/gitea-github-theme/core";

const themeColor: ThemeColor = {
  isDarkTheme: true,
  primary: "#0969da",
  primaryContrast: "#f0f6fc",
  secondary: "#30363d",
  base: { red: "#f85149", orange: "#d29922" /* ... */ },
  console: {
    /* ... */
  },
  diff: {
    /* ... */
  },
  other: {
    /* ... */
  },
  github: {
    /* ... */
  },
};

const syntaxColor: Syntax = {
  /* Custom code highlight colors, or import from another theme */
};

export default defineTheme({ colorType: "theme", themeColor, syntaxColor });

To use Gitea's native color format, use colorType: "gitea" with the GiteaColor type:

import type { GiteaColor, Syntax } from "@lutinglt/gitea-github-theme/core";
import { defineTheme } from "@lutinglt/gitea-github-theme/core";

const giteaColor: GiteaColor = {
  /* ... */
};
const syntaxColor: Syntax = {
  /* ... */
};
export default defineTheme({ colorType: "gitea", themeColor: giteaColor, syntaxColor });

After completing theme color development, register the theme series in theme.config.ts, compile it, and take screenshots in a Forgejo instance. Place them in the .github/assets/screenshots directory with filenames matching the theme names. (Cloning the repository is recommended to avoid leaking personal information.)

Then add screenshot information to README.md.

For color reuse patterns, see the colorblind theme implementation in themes/github.ts.

Theme Style Contributions

The TypeScript styles in styles/ use the css() template string for development. Strings are processed by LightningCSS and support CSS nesting syntax. Do not use SCSS functions; for complex processing, use TypeScript libraries such as the bundled color2k library.

For complex logic, it is recommended to extract it into functions under the src/functions directory.

Where new styles go

Choose the place by the markup a rule styles and the pages that need it:

  • Forgejo's own markup, on any page: a css fragment in styles/, under styles/public for base elements or styles/components for one area of Forgejo. It is built into every theme stylesheet, so installs without the templates get it too.
  • Reusable UI: a directory under components/<group>/<name>/ with its template partials, stylesheet, script and tests. Link its stylesheet and script in templates/base/component_assets.tmpl; bun bundle copies the files.
  • Layout of one page or a family of pages: plain CSS in styles/pages/<page>.css. Add the file to the list in scripts/assembleForgejo.ts and link it in templates/base/component_assets.tmpl.
  • An asset that one page template loads: public/<page>.css or public/assets/js/<page>.js, linked from that template. Styles that several pages share belong in a component.

Link assets as {{AssetUrlPrefix}}/css/... or {{AssetUrlPrefix}}/js/... without a version; the build adds the content hash.

Name classes by scope

Name the classes the theme adds by their scope:

  • theme-* for shared components, such as theme-search-field and theme-counter, with data-theme-* attributes as script hooks.
  • github-* for layout and state hooks on overridden templates, such as github-issues or github-embedded.
  • A page prefix such as profile-, org- or actions- for page-only CSS, such as profile-avatar-card.

Keep the stacking order

Keep stacked layers within the scale in components/common/motion/motion.css:

Layer z-index
Popovers and positioned page content up to 100
Repository tabs (.repository-tab-nav) 199
Navbar (#navbar) 200
Toasts 500
Dimmers and modals 1000 and up

Reuse shared page controls

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/component_assets.tmpl, included from 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 theme-sidebar, theme-sidebar-content, and theme-sidebar-main for its children. Include components/common/sidebar/toggle inside the sidebar and a button with data-theme-sidebar-toggle and data-theme-sidebar-expand beside the page title. The controller preserves desktop collapse preferences and starts collapsed on mobile. See templates/repo/issue/sidebar.tmpl and templates/org/repository_nav.tmpl. For links to sections of a long page, like GitHub's "On this page", add a nav with data-theme-sidebar-outline holding a theme-sidebar-label heading and #id links; the controller marks the section in view and, on a phone, closes the sidebar when an entry is followed. See templates/repo/release/sidebar.tmpl.
  • Search: call components/common/search/search with Value and Placeholder inside the existing form; do not write a search input in a page template. Kind selects the variant: submit (default, trailing search button), icon (leading search icon, as on releases and Actions), or plain. Name defaults to q, Label overrides the accessible name, and Class adds a page layout hook to the field. For a client-side filter that is never submitted, pass Local with an ID for its controller. For results that htmx loads while typing, pass Live (the URL) and LiveTarget (the results' selector), as the wiki search does. Keep hidden filter inputs in the form and size the field from the page stylesheet through its Class or parent; an unconstrained wrapper can shrink it. A control built by a script uses the theme-search-field class directly. Forgejo's native query/mode/submit group (shared/search/combo_multi) opts in with theme-search-control on its form.
  • Dropdowns: call components/common/dropdown/dropdown with Kind (single, multiple, or actions), Label, Items (the content template name), and Data (the page context). Title, Icon, IconOnly, UpdateLabel, Quiet, and Primary (a green trigger, as on GitHub's Code menu) customize the header and trigger; Class adds a page layout hook to the menu. Use components/common/dropdown/option for radio or checkbox rows; action rows are links or buttons with theme-dropdown-action. Single-choice menus use checkmarks, multiple-choice menus use native checkboxes, and action menus omit selection indicators. The controller handles Escape, outside clicks, and opening one menu at a time. See components/repository/filter/filter, components/repository/sort/sort, and org/action_menu_items.
  • Buttons: keep Forgejo's native ui button classes on buttons Forgejo already renders; the theme styles those globally. For a button the theme adds, use button theme-control (32px, 12px inline padding) instead of sizing it in 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. Add theme-counter-tab inside a tab, where GitHub's counter is 18px tall and semibold. Keep only layout (margin, flex) under the page's own class.
  • 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. Add theme-empty-compact for the smaller, borderless block GitHub shows inside a list. 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_package_versions.tmpl.

For repository filtering, add data-theme-repository-search to the form and render components/repository/results/results after it. Its results.js updates that result fragment through the same server-rendered GET route, preserves search and sort values, and cancels obsolete requests. Checkbox criteria combine, while opposing values of one criterion are exclusive. Clear filters is an action, not a selectable option. Keep tab=overview or tab=repositories in the organization form; query parameters alone do not determine the page layout. Use org/repository_pagination so pagination retains the same view even without the enhancement script. Errors leave the previous results visible and display a retry message; browser history restores the server-rendered controls and results.

After changing a component, verify each consuming page at desktop and mobile widths. Check search submission, retained filters, sidebar focus and collapse behavior, and Escape/outside-click dismissal for select menus.

Use theme tokens

All colors used in theme styles must use color variables. Import them via import { themeVars } from "@lutinglt/gitea-github-theme/core".

When using theme-specific color variables like ${themeVars.github.xxx}, please add the file and variable to the corresponding variable's comment in src/types/color/github.

For small border-radius values (6px), use the global border-radius variable. Import via import { otherThemeVars } from "@lutinglt/gitea-github-theme/core" and use ${otherThemeVars.border.radius}.