| 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 style overrides |
| styles/common | Reusable CSS style fragments |
| styles/public | Base element or cross-page element styles |
| styles/components | Page-specific styles |
| styles/templates | Template-specific styles |
| themes | Color theme definitions |
| primer | Auto-generated Primer design tokens |
| scripts | Build and utility scripts |
| templates | Gitea template overrides (optional) |
| gitea | Upstream Gitea reference files |
| screenshots | Theme screenshots |
Style contributions are not recommended, as Gitea themes are ultimately delivered as a single CSS file, 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 Gitea's style layout 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
Submit changes through a pull request targeting dev. Use a specific, release-note-friendly title in the form
Area: Description, for example Templates: Fix commit status badge rendering.
Apply exactly one primary release label:
featurefor new user-facing capabilities or themeschangefor intentional style, template, or compatibility improvementsbugfor regressions and incorrect renderingdependenciesfor dependency and build-tool updatesskip-changelogfor internal changes that should not appear in release notes
Add major when the change requires a major version bump. It modifies version resolution but does not create a separate
release-notes section.
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.
bun installCreate 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 and sync to remote server for preview:
bun bundle:devCompile all themes:
bun bundleFormat project code:
bun fmtFor PR submission — check and compile all project code with formatting:
bun commitsrc, 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"
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 Gitea instance. Place them in the 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 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.
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-layoutand a uniquedata-theme-sidebarstorage key to the page, then usetheme-sidebar,theme-sidebar-content, andtheme-sidebar-mainfor its children. Includeshared/theme/sidebar_toggleinside the sidebar and a button withdata-theme-sidebar-toggleanddata-theme-sidebar-expandbeside the page title. The controller preserves desktop collapse preferences and starts collapsed on mobile. Seetemplates/repo/issue/sidebar.tmplandtemplates/org/repository_nav.tmpl. - Search: call
components/common/search/searchwithValueandPlaceholderinside the existing form; do not write a search input in a page template.Kindselects the variant:submit(default, trailing search button),icon(leading search icon, as on releases and Actions), orplain.Namedefaults toq,Labeloverrides the accessible name, andClassadds a page layout hook to the field. For a client-side filter that is never submitted, passLocalwith anIDfor its controller. Keep hidden filter inputs in the form and size the field from the page stylesheet through itsClassor parent; an unconstrained wrapper can shrink it. A control built by a script uses thetheme-search-fieldclass directly. Forgejo's native query/mode/submit group (shared/search/combo_multi) opts in withtheme-search-controlon its form. - Dropdowns: call
components/common/dropdown/dropdownwithKind(single,multiple, oractions),Label,Items(the content template name), andData(the page context).Title,Icon,IconOnly,UpdateLabel, andQuietcustomize the header and trigger. Usecomponents/common/dropdown/optionfor radio or checkbox rows; action rows are links or buttons withtheme-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. Seecomponents/repository/filter/filter,components/repository/sort/sort, andorg/action_menu_items. - Buttons: keep Forgejo's native
ui buttonclasses on buttons Forgejo already renders; the theme styles those globally. For a button the theme adds, usebutton theme-control(32px, 12px inline padding) instead of sizing it in a page stylesheet, withtheme-control-widefor the 16px padding GitHub uses on profile pages ortheme-control-smallfor 28px controls. Usetheme-icon-buttononly for icon-only controls. Keep nativeprimary,secondary, and permission checks on actions, and leave page stylesheets to layout such asflexandorder. - Counter: add
theme-counterto a count pill next to a tab, heading, or filter, including ones a script creates. Addtheme-counter-tabinside a tab, where GitHub's counter is 18px tall and semibold. Keep only layout (margin,flex) under the page's own class. - Segmented switch: use
<nav class="theme-switch">with links andactiveon the current one, as on the releases and tags pages. - Empty state: use
theme-emptyfor a bordered "nothing here" box holding an icon, anh2orh3, and an optional paragraph.templates/user/profile_empty.tmplrenders one fromIcon,Title, andDescription. Addtheme-empty-compactfor 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 labelfor 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. Seetemplates/user/profile_packages.tmpl.
For repository filtering, add data-theme-repository-search to the form and render
components/repository/results/results after it. repository-search.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.
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}.