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

lawgic-dev-kit

v0.45.0

Published

Componentes de UI para Lawgic

Downloads

3,346

Readme

lawgic-dev-kit

The shared React component library behind Lawgic's frontends. Both apps consume it as a published npm package:

  • lawgic-pi — the customer-facing app
  • lawgic-management — the internal staff app

If a component is used by both — or clearly would be — it belongs here.


When does a component belong in this library?

This is the rule. Apply it before writing anything new.

It belongs in the library when it is used by more than one app, or clearly would be, and carries no app-specific data, routing, or domain vocabulary.

It stays local to the app when it is bound to that app's routes, Redux store, API services, or business language — marca, expediente, oficio, ticket, reporte.

When a component is nearly shared, split it. The presentational shell comes here; the data-bound wrapper stays in the app. DataTable and ToolHeader follow this split without living in the library yet — their skeleton is kept identical by hand between the two apps, while each app's own copy injects its own manager, filters and search through slots. See T-102-incompatible-components.md for the current state and why the skeleton has not moved here.

Some concrete calls that follow from the rule:

  • A component that fetches its own data does not belong here. If it needs data, it takes it as a prop or the caller passes a rendered node into a slot.
  • A component that imports from next/link, next/image or next/navigation does not belong here. The library is framework-agnostic; take a renderLink prop instead.
  • A component whose only difference between the two apps is styling tokens belongs here, with the difference resolved to a literal value.
  • Two components sharing a name but not a purpose are not duplicates. Do not merge them just because the names collide.

Anything that could not be unified is written down in T-102-incompatible-components.md at the workspace root, with the reason and what would have to change first. Read it before attempting to share Sidebar, SearchBar, MultiValueTextInput, HeaderFilter, FullLogo or status.ts.


Layout of the library

src/
  components/
    atoms/        smallest units — Button, Chip, Checkbox, TextInput, IconButton
    molecules/    compositions of atoms — DateInput, Dropdown, SideModal, Tooltip, AlertBanner
    organisms/    full sections — EmptyDialog, ImageViewer, PageShell
  hooks/          useTheme, useFilterToggler, useImageLoadStatus
  types/          shared contracts: pagination, filters, table, modal, toast
  themes/         the LawgicTheme shape and its default
  providers/      ThemeProvider
  contexts/       themeContext

Component folder pattern

Every new component follows the shape of atoms/Button/:

Button/
  Button.tsx          component body, default export at the bottom
  Button.types.ts     the exported props interface
  Button.styles.ts    className maps and resolvers — only when there is a real map
  Button.stories.tsx  Storybook story, required
  index.ts            export { default as Button } from './Button';
                      export type { ButtonProps } from './Button.types';

New folders re-export their props types from index.ts. Consumers need them at call sites, and several older components predate the convention and do not — do not copy that omission.

Barrels

The export chain is <Component>/index.tsatoms|molecules|organisms/index.tscomponents/index.tssrc/index.ts. Adding a component means adding one line to its layer's barrel.

The layer barrels are inconsistent about style — atoms/index.ts uses export * from "./X/index"; while molecules/index.ts uses export * from "./X";. Match the file you are editing rather than "fixing" it.

Imports inside src/components

Always relative: ../FloatingMenu, ../../atoms/Button. The @/* alias exists in tsconfig but is never used inside the component tree — do not introduce it.

Never deep-import through node_modules. A path like node_modules/lawgic-dev-kit/dist/... bypasses the exports map and breaks on the next build.


Build and publish

npm run lab        # Storybook on :6006 — the only visual verification this repo has
npm run build:lib  # the real library build
npm run lint
npx tsc --noEmit -p tsconfig.app.json

npm run build builds Storybook, not the library. It is a trap. The library build is build:lib, which runs vite build, then build:types, then scripts/build-consumable-css.mjs.

Type-checking must name the project explicitly: tsconfig.app.json is what build:types uses, and a bare tsc --noEmit picks a different one.

Publishing

azure-pipeline-prod.yml triggers on push to main, runs build:lib, then npm publish.

There is no npm version step. Bump version in package.json manually, as a commit on your branch, before it merges. A stale version fails the publish on a duplicate.

Both apps depend on a caret range, so mind what the bump means: on 0.x, ^0.25.4 resolves >=0.25.4 <0.26.0. A patch bump reaches both apps on their next install with no action; a minor bump does not, until someone raises the range in each app. Use that deliberately — a minor bump is the right choice when you want the apps to opt in rather than drift.

Installing

The library depends on FontAwesome Pro, which lives on a private registry. You need an .npmrc with the FontAwesome registry and a token before npm install will work. Do not commit that file.


Styling

Tailwind v4, CSS-first. src/index.css does @import 'tailwindcss', then @config '../tailwind.config.ts', then declares the dark variant.

The 10px base

html { font-size: 62.5% }, so the numeric scale keys are pixel values: spacing[24] is 2.4rem, which renders as 24px. p-24 means 24 pixels, not 6rem. Both apps replicate this exact scale, so classes port between the three repos unchanged.

Three traps that cost real debugging time

1. The apps do not scan this library for Tailwind classes. Their content globs cover only their own ./src/**. Library styles reach them solely through import 'lawgic-dev-kit/styles.css'. A class that does not resolve against this repo's tailwind.config.ts renders unstyled, with no build error anywhere.

App-only tokens are the usual cause. text-muted, border-divider, z-modal, z-dropdown, z-sidebar and z-filter-bar exist in lawgic-management and nowhere else — inside the library, use the literal value.

After adding a component, confirm its classes actually shipped:

grep -cF 'max-w-\[1600px\]' dist/lawgic-dev-kit.css   # 1
grep -cF 'max-w-[1600px]'   dist/lawgic-dev-kit.css   # 0 — wrong, and this is the trap

Two separate things bite here, and together they have already produced one false alarm during this migration:

  • Tailwind escapes brackets in the emitted selector, so the CSS literally contains .max-w-\[1600px\]. Your pattern has to include the backslashes.
  • Use grep -F. Without it, [1600px] is a regex character class and matches nothing you meant.

Get either wrong and you get a confident 0 for a class that shipped perfectly well. When a count comes back zero, re-check the pattern before you believe it — and confirm against a class you know is there.

The check can also lie in the other direction. Tailwind v4 scans the whole repo, this file included, so a class name written in prose here is enough to emit its rule. text-muted and border-divider were both in the stylesheet before any component used them, purely because the paragraph above names them. So a non-zero count proves the class compiles, not that your component uses it — to prove the component, grep src/ too, or pick a class nothing else mentions.

2. This library compiles with an older Tailwind than the apps. package.json declares tailwindcss: "latest", a non-reproducible spec that currently resolves to 4.0.17, while the apps run 4.2 and 4.3. Any utility introduced in 4.1 or later emits nothing here.

EmptyDialog hit this: it arrived carrying justify-center-safe, which works in the app and produces no rule at all from the library. The workaround is the arbitrary-property form, which compiles on any 4.x:

[justify-content:safe_center]     instead of     justify-center-safe

Pinning the library to ^4.1.12 is the real fix, but it regenerates the CSS of every component at once with no visual diff surface to check it against — it needs its own ticket.

3. User-facing strings should be props, not t() calls. useTheme() returns a t injected by each app. lawgic-pi wires real i18next with full catalogues; lawgic-management wires a hardcoded map of about a dozen keys. A key the map does not know renders as the raw key, silently.

So: layout and presentational components take their copy as ReactNode props. Reach for t() only when the key already exists in both bridges. Spanish literals are an acceptable default for aria-labels and fixed copy — the products are Spanish-only.

Theme

<ThemeProvider theme={{ defaultColorScheme: 'blue', t }}>

ThemeProvider also owns dark mode: it watches prefers-color-scheme and toggles dark on <html>. Components opt in with dark: variants.


Conventions

  • No comments in source. No //, no block comments, no JSDoc, no TODO markers. Rationale belongs in the PR description and the commit message. Comments that already exist stay — do not delete them as drive-by cleanup.
  • English for everything technical — identifiers, filenames, branches, commits. Neutral Mexican Spanish for anything a user reads, using tuteo ("puedes", "tu marca"), never voseo.
  • Small, atomic commits. One component per commit, including its story and its barrel line. Imperative one-line messages: Add the AlertBanner molecule, not added stuff.
  • A story for every new component. There are no tests and no PR pipeline here; Storybook is the only executable verification this repo has. Treat a missing story as a missing test.
  • No .test.tsx / .spec.tsx files unless someone asks for them.