Unified frontend monorepo for Kyber Network's DeFi products (Aggregator, Limit Orders, Zap Widgets, Cross-Chain Swaps).
pnpm i # Install dependencies
pnpm build-package # Build shared packages (REQUIRED before running apps)
cd apps/kyberswap-interface && pnpm dev # Run main interface| Command | Purpose |
|---|---|
pnpm i |
Install all dependencies |
pnpm build-package |
Build shared packages only |
pnpm build |
Build everything |
pnpm lint |
Run ESLint across all packages |
pnpm type-check |
TypeScript validation |
pnpm lint
pnpm type-check
pnpm build- React 18 + TypeScript + Vite
- pnpm workspaces + Turborepo
- Tailwind CSS (utility-first, via the
cn()helper fromutils/cn; avoid inlinestyleand styled-components — both are deprecated in this app) - Redux Toolkit + RTK Query (app state), zustand (widget state)
- wagmi, viem
Note — OG social-preview cards + per-route
<head>injection are handled by the separate kyberswap-og-service repo (a standalone Node/Hono service), not part of this monorepo.
- Functional components with hooks only
- TypeScript strict mode, no unjustified
any - Components:
PascalCase.tsx, Hooks:useCamelCase.ts, Utils:camelCase.ts - Import order: external libs →
@kyberswap/*→ local imports → types → styles
- Comments describe what the code currently does and why — never narrate change history. Avoid "now does X", "was Y", "no longer needed", "changed from…", "previously…", "instead of…", and similar.
- Never reference a value or approach that was tried and removed in the same branch (e.g. "a full core is no longer needed"). It won't appear in git history, so a future reader who sees only the final code can't make sense of it. Write in the present tense, focused on the code being annotated — git history is the changelog.
- Keep load-bearing "why" comments; drop comments that just restate the obvious.
When writing or modifying code, always ensure the result is free of:
- TypeScript errors/warnings — no
anyunless absolutely necessary (and justified with a comment), no implicitany, no unused variables/imports, no type mismatches. Run mental type-checking on every change. - ESLint violations — follow all project ESLint rules. Key rules include:
- No unused imports or variables (
unused-imports/no-unused-imports) - Proper React hooks dependency arrays (
react-hooks/exhaustive-deps) - Proper import ordering
- No unused imports or variables (
- Prettier formatting — all code must match the project's Prettier config (single quotes, trailing commas, semicolons, print width). Format code consistently.
Workflow (MANDATORY): After making any code changes, you MUST run the following commands on the affected package(s) and fix all errors before considering the task done:
pnpm lint # Fix all ESLint errors/warnings
pnpm type-check # Fix all TypeScript errorsFormat all modified files with Prettier. Do NOT present code changes as complete until these checks pass with zero errors. If any check fails, fix the issues and re-run until clean.
The app has been migrated off styled-components / rebass / polished. All new code MUST follow these rules.
- Always use theme tokens — never hardcode hex/rgb. Prefer Tailwind utility classes (
bg-primary,text-subText,border-border); checksrc/theme/color.tsandtailwind.config.tsfor existing tokens before adding new ones. - Key tokens:
primary,text,subText,background,border,red,warning,buttonBlack,buttonGray,tableHeader,gray,blue1-3,text2-6, etc. - Alpha-blended variants exist as named tokens:
subText-20,primary-10/12/15/20/25/30/40/50,red-10/20/25/30/35,warning-10/20/25/30/35,white-04/08/60,text-04/08/12/60,buttonBlack-40/60. Seetailwind.config.tsfor the full list. - For dynamic alpha not covered by a named variant, use Tailwind's opacity modifier (
bg-buttonGray/70,border-border/40,text-white/[0.92]) — supported on tokens whose Tailwind entries use the<alpha-value>placeholder. - Arbitrary
[#hex]is acceptable for one-off colors not in the theme; promote to a named token only if reused 3+ times.
- Always use
cn()fromutils/cnto compose classNames. Never use template literals (`foo ${cond ? 'a' : 'b'}`).cn()runstailwind-mergeso conflicting classes deduplicate; the prettier-plugin-tailwindcss can sort static strings inside it.cn(undefined)/cn(false)/cn('')are safe — no need for?? ''fallbacks.
- For components with ≥2 axes of style variants (size × variant, status × emphasis), use
cva(class-variance-authority) instead of nested ternaries incn(). Seecomponents/Badge/index.tsxandcomponents/SegmentedControl/index.tsxfor the pattern.
- Only use inline
stylefor genuinely runtime values:- JS variables and computed expressions (
width: size,transform: \rotate(${n}deg)``) - Computed colors (
hexAlpha(theme.X, runtimeAlpha)) - CSS variables on the consumer element (
--ks-scrollbar-thumb: ...) - framer-motion
animate/initial/variants/whileHoverprops (notstyle, but related) - Spread merges (
style={{ ...style, ... }})
- JS variables and computed expressions (
- For static colors / spacing / layout, use Tailwind classes — never inline.
- Only for runtime color strings consumed by non-DOM libraries: chart libs (recharts, lightweight-charts),
react-loading-skeleton'shighlightColor, framer-motionwhileHover/animatecolor values, third-party SVGstroke/fillprops. - NEVER for
style={{ color: theme.X }}/style={{ background: theme.X }}— use a Tailwind class instead.
- Reserve
hexAlpha(color, alpha)fromutils/colorAlpha.tsfor runtime computed colors (chart libs, runtime-dynamic backgrounds). - For STATIC theme color + STATIC alpha, use the Tailwind opacity modifier instead:
bg-warning/30,text-white/[0.92],bg-primary-10(named variant), etc.
- New icons in
components/Icons/must usecurrentColorforfill/strokeand accept onlyclassName(plus optionalsizeandstyle).- Default the visual color via a baked-in Tailwind class on the SVG (
text-subText,text-primary, etc.). - Consumers override via
<Icon className="text-X" />(orstyle={{ color: runtimeValue }}for dynamic colors).
- Default the visual color via a baked-in Tailwind class on the SVG (
- Multi-color brand marks (
OptimismLogoFull,PolygonLogoFull,PoweredByIconDark) are the exception — keep their hardcoded fills.
Row/RowBetween/RowFit/RowFixed/AutoRow/Column/ColumnCenter/AutoColumn/Stack/HStack/Centerare thinforwardRefdivs that ONLY acceptHTMLAttributes+className+ an optionalasprop (Stack family).- They do NOT accept shorthand props (
sx,mt,mx,px,gap,width,height,align,justify,direction, etc.) — control layout via Tailwind classes instead. - When introducing a new layout primitive, follow the same one-line pattern:
forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(({ className, ...rest }, ref) => <div ref={ref} className={cn('flex …', className)} {...rest} />).
When promoting a color to tailwind.config.ts:
- Define the CSS variable in
src/tailwind.cssunder:root(e.g.--ks-newToken: #abcdef;). - For tokens that should support Tailwind's
/Nopacity modifier, also add an RGB-triple variable (--ks-newToken-rgb: 171 205 239;). - In
tailwind.config.ts, register the token as'newToken': 'rgb(var(--ks-newToken-rgb) / <alpha-value>)'(or'var(--ks-newToken)'if no opacity modifier is needed). - Export the matching key from
src/theme/color.tssouseTheme()returns it (for chart-lib / runtime use).
- Only justified for: third-party library overrides (CKEditor,
@reach/dialog,@near-wallet-selector), scrollbar vendor pseudo-elements, or keyframes that don't fittailwind.config.tskeyframes. - Shared keyframes and one-off
ks-*utilities live insrc/tailwind.cssunder@layer components. Per-feature CSS files have been removed. - For scrollbar styling, use the
.ks-scrollbarutility (intailwind.css) and override its CSS variables (--ks-scrollbar-width,--ks-scrollbar-thumb,--ks-scrollbar-radius) via inline style on the consumer.
- Always reuse existing formatters — before writing any number formatting logic, check the existing utilities:
formatDisplayNumber()fromutils/numbers.ts(app-level, supportsdecimal/currency/percent)formatDisplayNumber(),formatTokenAmount(),formatCurrency(),formatNumber(),formatWei(),formatAprNumber()from@kyberswap/utilsformatTokenBalance(),formatBigLiquidity(),formatLongNumber()fromutils/formatBalance.ts
- Do not use deprecated formatters (
formatDollarAmount,formattedNum,formatCurrencyAmount).