npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

lily-lark-ui

v0.10.2

Published

Thư viện component React theo phong cách Lark (Universe Design) cho React 19 + Tailwind v4. 40 component + `LarkMotionProvider` (bật/tắt chuyển động), nền Radix headless, phát hành trên npm.

Readme

lily-lark-ui

Thư viện component React theo phong cách Lark (Universe Design) cho React 19 + Tailwind v4. 40 component + LarkMotionProvider (bật/tắt chuyển động), nền Radix headless, phát hành trên npm.

npx lily-lark-ui init        # cài vào app hiện tại (tự cài Tailwind v4)

Phiên bản hiện tại: 0.10.2. Mọi component nhận đủ prop HTML của phần tử gốc (onBlur, onFocus, onKeyDown, name, form, value, data-*, aria-*, style, id, ref) — xem mục Prop HTML.

Cài đặt

Xem INSTALL.md để biết đầy đủ. Tóm tắt 2 đường:

  • CLI (khuyến nghị): npx lily-lark-ui init — tự cài Tailwind v4, sửa vite.config.ts, chèn CSS + @source.
  • npm thủ công: pnpm add lily-lark-ui rồi tự thêm 3 dòng CSS.
  • script: scripts/install.sh --repo <path> --apply (nếu dùng source checkout).

Yêu cầu bắt buộc

| Thứ | Phiên bản | Ghi chú | |---|---|---| | Tailwind CSS | v4 (^4.0.0) | Token dùng @theme/@utility của v4. v3 không chạy. | | @tailwindcss/vite | ^4.0.0 | Plugin build (không dùng PostCSS config kiểu v3). | | React / React DOM | ^19.0.0 | peerDependency. | | Node | >= 20 | ESM + Vite hiện hành. |

npm i -D tailwindcss@^4 @tailwindcss/vite

Ba dòng bắt buộc trong entry CSS

Tailwind v4 không tự quét node_modules. Thiếu @source thì component render ra không có style (nút trong suốt, sai chiều cao) dù build không báo lỗi.

@import "tailwindcss";
@import "lily-lark-ui/styles.css";
@source "../node_modules/lily-lark-ui/src";

@import "tailwindcss" phải đứng trước import của thư viện. Nếu app đã có CSS riêng, bọc CSS đó vào @layer base để không đè utility của component.

Kiểm tra sau khi build: grep -c 'bg-brand-secondary' dist/assets/*.css phải lớn hơn 0.

Cai vao repo moi

Mot lenh (6 buoc + kiem Tailwind v4, copy --components neu co, check-lark report):

npx lily-lark-ui init                      # app hien tai
npx lily-lark-ui init --repo ../app        # app khac
npx lily-lark-ui init --dry-run            # chi in ke hoach
npx lily-lark-ui init --no-bootstrap       # khong dung Tailwind/vite config
npx lily-lark-ui init --components button,table

Hoac goi thang script trong source checkout:

scripts/install.sh --repo <path> --apply [--components button,text-field]
  • Khong truyen --apply thi dry-run: chi in ke hoach, khong sua file.
  • --skip-install: bo qua buoc cai package (dung cho test offline).
  • --components <list>: chi copy component can (ten cach nhau boi dau phay, vd button,text-field); kem src/lib/utils.ts + src/styles/lark-tokens.css neu chua co. Ten sai thi bao loi + liet ke ten hop le.
  • Idempotent: chay lai khong them trung import/block MCP.

Phát hành (maintainer)

pnpm release:dry   # build + test + check-lark + publint + pack + import(), khong publish
pnpm release       # publish thật, rồi cài lại từ registry để xác nhận

Package là lily-lark-ui (không scope). Publish cần token npm có quyền ghi package. Token hiện bị giới hạn cho staging area: npm publish trả E_STAGE_REQUIRED, khi đó scripts/publish.sh tự chạy npm stage publish và in npm stage approve <stage-id> — bước approve cần 2FA của chủ tài khoản. Stage xong chưa lên npm; bản mới cũng đi qua automated review của npm (vài phút) trước khi thành latest.

Component

Tất cả component nhận đủ prop HTML của phần tử gốc; bảng dưới chỉ liệt kê prop riêng của thư viện.

| Component | Props riêng | Số đo | |---|---|---| | LarkButton | variant (primary, secondary, secondaryBlue, ghost, text, primaryError, secondaryError), size (xs/sm/md/lg), isIconOnly, fullWidth, isPending, asChild | 32px mặc định (h-8), 40px nút chính form (h-10), 28/24 nhỏ; mobile 36/48px | | LarkTextField (+ inputClass) | label, error, description, isRequired, isInvalid | 42px mobile / 36px PC (md:min-h-9), chữ 16/14 | | LarkTextArea | label, error, isRequired | tối thiểu 72px (~3 dòng) | | LarkDropdownMenu | trigger, align (start/center/end); Item (value, onSelect, danger, disabled, checked); Separator | popover min 200 rộng, bo 8, bóng overlay; item 32px; đóng menu trả focus về trigger | | LarkTabs | defaultValue/value/onValueChange; List (showIndicator mặc định true — tự chèn vạch trượt), Tab, Panel, Indicator (variant underline/pill) | tab cao 34, vạch trượt 3px, tab chọn không nền | | LarkTooltip (+ LarkTooltipProvider) | content (ReactNode), side, delayDuration, triggerClassName; Provider delayDuration (=300), skipDelayDuration | delay 300ms, lệch 6px, overlay 300ms | | LarkSelect | options, value/onValueChange, onOpenChange, placeholder, disabled, isInvalid, ariaLabel, containerClassName, onClear, required | trigger 32px bo 6, item 32px, dropdown bo 6; có onClear thì hover/focus hiện nút xoá như picker ngày | | LarkDatePicker | label, value (YYYY-MM-DD), onChange, onClear, error, isInvalid, required, placeholder, disabled, describedBy, containerClassName | trigger 32px như Select, panel bo 6, lịch 42 ô tuần bắt đầu T2, ô ngày 32px | | LarkMonthPicker (+ formatMonthValue) | label, value (YYYY-MM), onChange, onClear, error, isInvalid, required, placeholder, disabled, describedBy, containerClassName | trigger 32px, lưới tháng 3 cột, bảng năm 12 ô | | LarkCheckbox | label, checked (true/false/'indeterminate'), onCheckedChange, controlClassName | khung 16px bo 4, dấu 12px trắng | | LarkRadioGroup | label, options hoặc children (LarkRadioGroup.Item), value/onValueChange | khung 16px tròn, chấm viền brand 5px | | LarkSwitch | label, checked/onCheckedChange, controlClassName | rãnh 36×20, núm 16px | | LarkDialog | open/onOpenChange; Content (title, description), Footer | bo 8, đệm 20/24, tối đa max-w-md | | LarkDrawer | open/onOpenChange; Content (title, description, position right/bottom, size sm/md/lg, actions, footer, showClose, closeLabel, overlay, bodyClassName) | phải 350/480/680 cao hết viewport; đáy rộng hết, trần 85dvh, bo trên 8; mask 55%; header 56, đệm 24 | | LarkAlertDialog | title, confirmLabel, cancelLabel (=Hủy), onConfirm, isPending | như dialog; pending chặn Escape, disable nút huỷ | | LarkToastProvider + useLarkToast | toast({ title, description, variant }); Provider duration (=4000), toastClassName, viewportClassName | nền n900, bo 8 / pill mobile, 4s, quét phải để tắt | | LarkSpinner / LarkEmptyState | Spinner size (sm/md), label bắt buộc; EmptyState children (một câu trong thẻ <p>), illustration? (mặc định ảnh LILY) | spinner 16/24px; empty căn giữa, mọi chữ một tông n400 | | LarkEmptyIllustration | size (sm/md/lg), label | ảnh giữ chỗ LILY LOOK & CARE tông n400, rộng 64/88/128px, tỉ lệ 140:81 | | LarkTable (+ Wrapper/Footer/TruncatedCell) | columns, rows, rowKey, isLoading, emptyState, onRowClick, rowClassName, ref; cột có width, align (left/right/center), pin: 'right' \| 'left', rowHeader, className, headerClassName; bảng có groupedHeader (nhóm rowSpan: 2 nhận width), density (default/compact), tableLayout (auto/fixed), caption, captionHidden, sectionRow, rowCells (hàng tự khai colSpan/rowSpan), sectionRowClassName, showHeader | head 48, body 47, divider n200/n300, ghim z-10/z-20 | | LarkPagination (+ pageWindow) | page, total, pageSize, onPageChange, pageSizes (=[50,100,200]), onPageSizeChange, isLoading | nút 28, active 28×28 bo 6, footer py-2 px-3 | | LarkTag | color (blue/gray/success/danger/warning), size (md/sm) | 24 md / 16 sm, bo 4 | | LarkStatus | label (bắt buộc), tone (success/warning/danger/info/neutral), variant (tag: chữ có nền / icon: chỉ icon không nền), size (md/sm), icon | hai dạng cùng một trạng thái; dạng icon role="img" + aria-label, icon 14/12 | | LarkBadge | count, max (=99), dot | cao 14, chấm 8px | | LarkAvatar (+ getInitials) | name, src, team, size (sm/md/lg) | 20/32/36px, tròn | | LarkNotice | variant (info/success/warning/error), title, icon, action, onClose | thông báo thường trực, icon 16, đệm 12/16; role là alert (warning/error) hoặc status (info/success), không tự tắt | | LarkBreadcrumb | items (label, href), separator, label (aria-label) | gap cấp 4px, icon phân cách 16px, cấp cuối aria-current="page" | | LarkPageHeader | title, description, breadcrumb, actions, titleId | tiêu đề <h1> 16px/24, cách breadcrumb 4px | | LarkProgress | value, label, variant (linear/circular), isIndeterminate, showValue, size, status (normal/success/error) | thanh 4px bo 2px, nhãn cách 12px; vòng 16px, nhãn cách 8px, chữ 12px; không xác định thì bỏ aria-valuenow | | LarkMotionProvider | disabled (mặc định false) | bọc root; disabled tắt toàn bộ animation/transition của thư viện trong cây con | | LarkCard | title, description, actions, footer, isHoverable | viền N300 1px, bo 8, đệm 12, cách tiêu đề–nội dung 12; hover mượn bóng overlay | | LarkStatCard | label, value, unit, hint, note, delta, deltaLabel, sparkline, isCountUp, isLoading | số lớn 24px, delta 12px + icon 14px; đếm tăng 900ms; value={null} hiện "Chưa xác định" | | LarkKeyValueList | items (label, value, emphasis, missing), orientation (inline/stacked), size (sm/md), divided | nhãn 12px N600, giá trị 12px; md 14px; phân cách 1px N200 | | LarkChecklist | items (label, value, status done/pending/missing/error), title, label, size (sm/md) | icon 14px + chữ trạng thái sr-only; nhãn 12/14px | | LarkChartSparkline | values, width, height, tone (neutral/positive/negative), label | mặc định 120×32, nét 2, không trục | | LarkChartBar | data (label, value, color), orientation, height, maxValue, showValues, caption, label | viewBox 640×160, lưới 1px, nhãn 12px, bo đầu cột 4px, cao tối thiểu 2px | | LarkChartLine / LarkChartArea | labels, series (name, values: (number \| null)[] — null = kỳ không có số, đường cắt đoạn; color), height, area, showLegend, showDots, caption, label | 4 mốc lưới, đường nét 2, chú giải dưới chart; giới hạn mềm 5 series (line) / 3 (area) | | LarkChartDonut | segments (label, value, color), size, thickness, centerValue, centerLabel, showLegend, caption, label | 160×160 mặc định, vành dày 24, số giữa 20px; giới hạn mềm 9 đoạn | | LarkSteps | items (title, description, icon, status), current, orientation (horizontal/vertical), label | nút 24px, đường nối 1px, cách bước 24px; ngang từ md, dưới md xếp dọc | | LarkTimeline | items (title, time, description, icon, status), label | chấm 8px, cột thời gian 96px ở md, cách item 16px |

Prop HTML

Mọi component kế thừa prop HTML của phần tử gốc, nên gắn được logic React thoải mái:

<LarkSwitch
  label="Nhắc lịch hẹn"
  name="notify"
  checked={on}
  onCheckedChange={setOn}
  onBlur={trackBlur}
  data-testid="notify-switch"
  aria-describedby="notify-hint"
/>

<LarkTable
  ref={tableRef}
  columns={cols}
  rows={rows}
  rowKey={(r) => r.id}
  onRowClick={(row, i) => open(row)}
  rowClassName={(row) => (row.urgent ? 'bg-lark-danger/5' : undefined)}
/>

name/form/value của LarkSwitch và LarkCheckbox vào hidden input của Radix nên FormData trong <form> thu được giá trị thật.

Chuyển động

Animation UI nằm trong src/styles/motion.css và lấy số từ token --motion-* trong src/styles/lark-tokens.css; component không tự viết duration/easing. File CSS của package tự import motion.css, không cần khai thêm gì trong app. Chuyển động dữ liệu (số đếm, thanh tiến độ, biểu đồ) không dùng recipe CSS: tất cả đi qua hook useReveal trong src/lib/use-count-up.ts, cùng một thời lượng --motion-duration-reveal, nên tải lại trang là toàn bộ chạy lại cùng lúc.

| Recipe | Vào | Ra | Dùng cho | | --- | --- | --- | --- | | motion-fade | 120ms | — | lớp phủ, notice, tag hiện ra | | motion-popover | 180ms, scale .96 | 100ms | select, dropdown, picker | | motion-dialog | 240ms, dịch 8px | 100ms | dialog, alert-dialog | | motion-toast | 240ms | 100ms | toast | | motion-tooltip | 120ms | 100ms | tooltip | | motion-press | active:scale(.98) | — | button, icon button | | motion-indicator | transform + translate 240ms | — | tab indicator, switch, radio | | motion-list-item | 180ms + trễ 20ms/dòng | — | danh sách ≤ 12 dòng | | motion-expand | grid-template-rows 240ms | 240ms | accordion, chi tiết | | motion-skeleton | pulse 1,2s | — | skeleton | | motion-spin | 800ms/vòng, linear | — | spinner | | motion-row-flash | giữ 360ms rồi mờ dần 840ms | — | hàng bảng vừa lưu |

Quy ước: chỉ animate opacity/transform; đường ra luôn ngắn hơn đường vào (100ms); không overshoot; prefers-reduced-motion: reduce tắt hết (riêng motion-row-flash giữ nền tô sáng, motion-spin đổi sang nhịp mờ dần). Tham chiếu đầy đủ: specs/motion.md.

Mặc định bật, tắt được. Mọi component có chuyển động tự chạy animation, app không phải thêm class; LarkTabs.List tự chèn vạch trượt (không cần đặt LarkTabs.Indicator). Tắt cả thư viện bằng provider ở root:

<LarkMotionProvider disabled>
  <App />
</LarkMotionProvider>

Provider đặt data-lark-motion="off" (wrapper display: contents, không phá layout) và useReveal trả thẳng giá trị thật, nên số đếm/thanh/biểu đồ hiện đúng ngay. Tắt riêng vạch trượt của tab: showIndicator={false}.

Nguồn số đo

Số đo lấy từ style guide chính thức của Lark (MCP lark-design: design-language/*, components/*) — đây là nguồn quyết định. File Figma (~/Sites/lily-projects/lark-design-guide/figma/) chỉ để đối chiếu; lệch thì theo style guide và ghi rõ chỗ lệch trong spec. Chi tiết: docs/specs/2026-11-05-lark-ui.md và specs/*.md.

Ví dụ

import { LarkButton, LarkTextField } from 'lily-lark-ui'

export function LoginForm() {
  return (
    <form>
      <LarkTextField label="Số điện thoại" placeholder="Nhập số điện thoại" isRequired />
      <LarkButton type="submit">Đăng nhập</LarkButton>
    </form>
  )
}
import { LarkTable, LarkTableWrapper, LarkPagination } from 'lily-lark-ui'

export function RecordsTable({ columns, rows, page, total, pageSize, onPageChange }) {
  return (
    <LarkTableWrapper>
      <LarkTable columns={columns} rows={rows} rowKey={(r) => r.id} />
      <LarkTable.Footer>
        <LarkPagination page={page} total={total} pageSize={pageSize} onPageChange={onPageChange} />
      </LarkTable.Footer>
    </LarkTableWrapper>
  )
}

Xem component

Mở lark-ui-components.html ở root repo bằng trình duyệt — một file tự chứa, có nút chuyển sáng/tối, responsive 320–1600px.

Dùng từ agent: MCP và skill

Repo có sẵn MCP server (mcp/server.mjs, stdio, không dependency ngoài) đọc trực tiếp specs/ và src/styles/lark-tokens.css:

| Tool | Trả về | |---|---| | lui_props {name} | Bảng prop của component (nhận LarkButton, LarkChartLine, LarkDrawer.Content, switch, specs/table.md) | | lui_get {name} | Toàn bộ trang spec: số đo, class mapping, ví dụ | | lui_tokens | Bảng token code từ src/styles/lark-tokens.css (màu, bo góc, chuyển động, bóng đổ) | | lui_search {query} | Tìm theo từ khoá, không dấu tiếng Việt vẫn khớp | | lui_list | Danh sách trang spec kèm tiêu đề |

"mcp": { "lark-ui": { "type": "local", "command": ["node", "/path/to/lark-ui/mcp/server.mjs"] } }

Skill lark-ui nằm trong skill/lark-ui/SKILL.md, phát hành kèm package; scripts/install.sh symlink vào thư mục skill của agent khi cài vào app.

Phát triển

Xem AGENTS.md trước khi sửa code.

pnpm test              # 431 test (vitest + jsdom)
pnpm test:visual       # 9 test layout thật (Playwright)
pnpm check:lark        # cấm hex, dark:, emoji-icon trong src/
pnpm showcase:build    # build lại lark-ui-components.html

Git hook trước khi push

Repo có .githooks/pre-push: mỗi lần git push (CLI, IDE hay agent) đều chạy pnpm build, pnpm check:lark, pnpm test trước khi đẩy. Hook không tự sửa file: nếu lark-ui-components.html lỗi thời, test sẽ FAIL và bạn chạy pnpm showcase:build rồi commit lại.

  • Bật: pnpm install tự chạy prepare → node scripts/install-hooks.mjs → git config core.hooksPath .githooks. Dev mới chỉ cần setup dự án một lần.
  • Kiểm tra: git config --get core.hooksPath phải in .githooks.
  • Bật tay khi cần: node scripts/install-hooks.mjs (clone cũ đã cài từ trước, hoặc pnpm install báo "Already up to date" — pnpm bỏ qua prepare khi không cài gì; pnpm install --force cũng chạy lại prepare).
  • Bỏ qua một lần khi thật cần: git push --no-verify.
  • Hook là kiểm tra local, không thay CI: người khác có thể --no-verify, và core.hooksPath không đi theo repo. Muốn chặn ở remote thì cần job CI.

Lưu ý: khi đang sửa giao diện, dùng pnpm showcase:dev (port 5210, HMR) để xem ngay; file lark-ui-components.html chỉ đổi sau pnpm showcase:build, và tab đang mở phải Ctrl+Shift+R mới thấy bản mới.