Contribution guide
Contributions to @lh-ui/share are welcome. This page is the hard contract for participating — most of these conventions are not style preferences but preconditions for the whole thing to work across four UI libraries.
The six red lines
Section titled “The six red lines”1. No framework dependencies in src/
Section titled “1. No framework dependencies in src/”// ❌ absolutely forbiddenimport { ref } from 'vue'import { useState } from 'react'import { ... } from '@dcloudio/uniapp'
// ✅ only pure functions and types are allowedexport function computeSomething(input: Foo): Bar { ... }Why: share is the family’s only framework-agnostic shared layer. One import 'vue' and the React build and the uniapp build are both broken. CI has a grep gate.
Reactive wiring belongs to each UI layer (ref() / useState() / useSyncExternalStore()); core only outputs pure data. See the lhlocale design: a pure event emitter that each port converts into its own re-render signal.
2. styles/ is the single source of component styles
Section titled “2. styles/ is the single source of component styles”- Copying the styles into any UI library for hand-editing is forbidden.
- All style changes happen only in this repo.
Cross-port transition class-name differences are unified by the three transition mixins; component styles must never hand-write duplicate class names — always @include. Pass the second argument when the state needs to live on a descendant node.
3. A single copy of the core unit tests
Section titled “3. A single copy of the core unit tests”tests/unit/ is the test result shared by all three libraries. UI libraries must not copy this repo’s core unit tests.
4. 1px hairline borders must carry a /* hairline */ marker — deprecated
Section titled “4. 1px hairline borders must carry a /* hairline */ marker — deprecated”/* hairline */ markeruniapp v3 outputs px directly and no longer transpiles rpx, so the hairline marker lost its purpose (see docs/bugfix-log.md D-02). The 65 historical markers already inside styles/ remain as ordinary comments; new 1px borders need no marker. Structural borders (2px / 3px) were never marked in the first place.
5. Release order: share first, UI libraries after
Section titled “5. Release order: share first, UI libraries after”share change → changeset → release → each library pnpm up @lh-ui/share plus a regression pass. This cannot be skipped — the UI libraries’ releases depend on the already-published share tarball.
6. Self-authored symbols must carry the LH prefix
Section titled “6. Self-authored symbols must carry the LH prefix”Follow this when adding constants / types / interfaces; do not leave it for a later unified cleanup (the historical rename is in the lh-prefix-naming changeset).
Naming conventions
Section titled “Naming conventions”Aligned with the plus repo’s docs/project/development-guide.md; uniform across the whole family:
| Layer | Convention | Example |
|---|---|---|
| Reverse-DNS root | top.lhgb (www.lhgb.top reversed) |
the naming root of every repo in the family; package.json’s homepage / repository / author use lhgb.top |
| JS / npm package names | the @lh-ui/* scope |
npm package names cannot contain dots, so the reverse-DNS name lives at the metadata level |
| Constants | LH_XXX, LH_<Module>_<Concept> |
LH_SIZE_DEFAULT, LH_THEME_PRESETS |
| Default-value constants | the DEFAULT suffix |
LH_DRAWER_DIRECTION_DEFAULT |
| Types / interfaces | LhXxx |
LhThemePreset, LhSxProps |
| Module-level singletons / lowercase objects | lhXxx |
lhlocale |
| BEM class names | lh-xxx |
lh-button__text--disabled |
| Component name | LhXxx |
name: 'LhButton' |
Registered exemptions
Section titled “Registered exemptions”Established by convention and not forced to take the prefix (register the reason here before changing any of them):
- Language-pack exports
zhCN/enUS— standard locale naming, and re-exported as a public API by all three UI libraries; renaming would break too much. - The string value of theme preset names (
name: 'cerblue'etc.) — this is the configuration value users write in ConfigProvider’stheme; changing it breaks every downstream user’s configuration. The corresponding TS identifier still takes the prefix (LhCerblue). - Exported symbols of third-party dependencies are not enforced.
Directory responsibilities
Section titled “Directory responsibilities”| Directory | Responsibility |
|---|---|
src/core/<name>/ |
Component pure-function logic (logic.ts / types.ts / index.ts), zero framework imports |
src/core/shared/ |
The cross-component kernel (12 modules; see the overview) |
src/theme/ |
The theme TS side (types + five presets) |
src/constants/ / src/locales/ / src/types/ / src/utils/ |
constants / language packs / types / pure utilities |
styles/ |
The single source of component BEM styles (<name>/index.scss + theme/common/*.scss) |
tests/unit/ |
The single copy of the core unit tests |
scripts/ |
Build and verification scripts |
docs/ |
The docs site (Astro Starlight); not part of the npm output |
Commit-message conventions
Section titled “Commit-message conventions”- Comments and commit messages are written in Chinese.
- Commit messages are written as detailed markdown: a title line + scope description + a list of added / modified files + a design-points table + verification status.
- Prettier config: single quotes, no semicolons, 2-space indent, printWidth 100.
pnpm formatonly covers config files; everything else is hand-aligned.
Checklist for adding a core component
Section titled “Checklist for adding a core component”- The
src/core/<name>/{logic,types,index}.tstriplet tests/unit/core/<name>.spec.ts- Add the export to
src/core/index.ts styles/<name>/index.scss(otherwise one port gets no style artifact — a silent skip)- A changeset (minor)
Then run:
pnpm test && pnpm lint && pnpm typecheckpnpm build # sync:exports generates the new exports keyspnpm verify:dts && pnpm verify:stylesSelf-check list before opening a PR
Section titled “Self-check list before opening a PR”- No
import 'vue' | 'react' | '@dcloudio/*'anywhere insrc/(red line 1) - Style changes are only in
styles/(red line 2) - Colors always take the form
var(--lh-*, fallback); no new hardcoded color values - New transition class names use the mixins; no bare
-enter-from/-leave-from - New tokens are in
var.scss; if the value is a#{$lh-*}interpolation,var-dark.scsshas the matching override - New symbols carry the
LH/Lhprefix (red line 6) -
pnpm test/pnpm lint/pnpm typecheckpass - After
pnpm build,pnpm verify:dts/pnpm verify:stylespass - There is a changeset, with the correct level
- The new component has
styles/<name>/index.scssandtests/unit/core/<name>.spec.tsexists (red line 3) - If tokens / class names / icons changed,
pnpm docs:datawas run to refresh the docs data
Known TODOs (recorded as follow-up changesets; claims welcome)
Section titled “Known TODOs (recorded as follow-up changesets; claims welcome)”The “control-height token” project:
- Control heights are inconsistent across components (input 37 / select 32 / cascader 27→32 / date-time 33 / input-number 32); the root cause is that
var.scsshas no--lh-control-height-*tokens - input’s asymmetric
padding-top: 16pxunder--has-label - spinner and icon disagree on same-named size tiers (small 16 vs 14)
- The checkbox checkmark is fixed at
top:2/left:5instead of 50%-centered (correct under a 16px box; any size change offsets it) - switch’s
translate(-50%,-50%)is hardcoded twice, in both the base rule and the keyframes
Related documentation
Section titled “Related documentation”- Project introduction — where share sits in the family
- Naming conventions — the full version of the naming rules
- Style injection per port — the mechanism behind red line 2
- Verification system — the checks to run before a PR
- Release process — the full flow behind red line 5
Copyright © 2024-2026 FengMo WenXue