Critical architecture knowledge for the squared component library package...
The @lsst-sqre/squared package is a React component library with a unique architecture designed for flexibility and type safety.
ā ļø The squared package does NOT have a build step - it exports TypeScript source files directly.
This means:
main, module, and types all point to src/index.ts (not a dist/ directory)tsup, tsc, or other build tools in the squared packageSee the actual package configuration at packages/squared/package.json.
Key fields:
{
"main": "./src/index.ts",
"module": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": {
"types": "./src/index.ts",
"default": "./src/index.ts"
},
"./components/*": "./src/components/*"
},
"sideEffects": false
}
Important:
sideEffects: false enables tree-shakingā ļø Squared package MUST use CSS Modules - NO styled-components allowed.
Why:
Pattern:
MyComponent/
āāā MyComponent.tsx
āāā MyComponent.module.css
āāā MyComponent.stories.tsx
āāā MyComponent.test.tsx
āāā index.ts
Styles use design tokens from @lsst-sqre/rubin-style-dictionary and @lsst-sqre/global-css.
See the design-system skill for complete CSS variable reference.
/* MyComponent.module.css */
.container {
padding: var(--sqo-space-md);
background-color: var(--rsd-color-primary-600);
border-radius: var(--sqo-border-radius-1);
box-shadow: var(--sqo-elevation-md);
}
.title {
font-size: 1.125rem;
font-weight: 600;
color: var(--rsd-component-text-color);
}
Available via:
packages/rubin-style-dictionary/dist/tokens.css - Foundation tokens (prefix: --rsd-*)packages/global-css/src/tokens.css - Application tokens (prefix: --sqo-*)@lsst-sqre/global-css in your app to load all tokensApps that use squared must configure transpilation.
Required in next.config.js:
module.exports = {
transpilePackages: ['@lsst-sqre/squared'],
// ... other config
};
This tells Next.js to transpile the squared package's TypeScript source.
See consuming-app-setup.md for complete setup guide.
If you forget to add transpilePackages, you'll see errors like:
Module parse failed: Unexpected token
You may need an appropriate loader to handle this file type
Squared uses vitest with two separate test projects:
.test.ts files)See the actual test configuration at packages/squared/vitest.config.ts.
# Unit tests only
pnpm test --filter @lsst-sqre/squared
# Storybook tests only
pnpm test-storybook --filter @lsst-sqre/squared
# Storybook tests in watch mode
pnpm test-storybook:watch --filter @lsst-sqre/squared
# All tests (run from root)
pnpm test
pnpm test-storybook
Unit tests:
src/test-setup.tsStorybook tests:
.storybook/vitest.setup.tsPrefer type over interface:
// ā
Good
type MyComponentProps = {
title: string;
onClick?: () => void;
};
// ā Avoid (unless extending/merging needed)
interface MyComponentProps {
title: string;
onClick?: () => void;
}
Avoid React.FC - type props directly:
// ā
Good
export default function MyComponent({ title, onClick }: MyComponentProps) {
return <div onClick={onClick}>{title}</div>;
}
// ā Avoid
const MyComponent: React.FC<MyComponentProps> = ({ title, onClick }) => {
return <div onClick={onClick}>{title}</div>;
};
// MyComponent/MyComponent.tsx
import styles from './MyComponent.module.css';
type MyComponentProps = {
title: string;
variant?: 'primary' | 'secondary';
};
/**
* Component description for documentation
*/
export default function MyComponent({
title,
variant = 'primary'
}: MyComponentProps) {
return (
<div className={styles.container} data-variant={variant}>
<h2 className={styles.title}>{title}</h2>
</div>
);
}
// MyComponent/index.ts
export { default } from './MyComponent';
export type { MyComponentProps } from './MyComponent';
// src/index.ts
export { default as MyComponent } from './components/MyComponent';
export type { MyComponentProps } from './components/MyComponent';
Squared depends on other monorepo packages:
@lsst-sqre/global-css - Global styles and design token application@lsst-sqre/rubin-style-dictionary - Design tokens@lsst-sqre/eslint-config - Linting configuration@lsst-sqre/tsconfig - TypeScript configurationThese use workspace protocol: "@lsst-sqre/global-css": "workspace:*"
# Start Storybook dev server
pnpm storybook --filter @lsst-sqre/squared
# Build static Storybook
pnpm build-storybook --filter @lsst-sqre/squared
// MyComponent.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import MyComponent from './MyComponent';
const meta: Meta<typeof MyComponent> = {
title: 'Components/MyComponent',
component: MyComponent,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof MyComponent>;
export const Default: Story = {
args: {
title: 'Example Title',
variant: 'primary',
},
};
export const Secondary: Story = {
args: {
title: 'Example Title',
variant: 'secondary',
},
};
With @storybook/addon-vitest, stories can include tests:
import { expect, within } from '@storybook/test';
export const WithTest: Story = {
args: {
title: 'Test Title',
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await expect(canvas.getByText('Test Title')).toBeInTheDocument();
},
};
Run with: pnpm test-storybook --filter @lsst-sqre/squared
src/components/.tsx, .module.css, .stories.tsx, .test.tsx filesindex.ts with exportssrc/index.tsCause: App not configured to transpile squared package.
Solution: Add to next.config.js:
transpilePackages: ['@lsst-sqre/squared']
Cause: CSS Module not imported or class name mismatch.
Solution:
import styles from './Component.module.css';className={styles.className}.className definedCause: @lsst-sqre/global-css not imported in app.
Solution: Import in app's root layout/component:
import '@lsst-sqre/global-css';
Cause: Type resolution issues with direct source imports.
Solution: Ensure consuming app's tsconfig.json includes squared source:
{
"include": ["src", "node_modules/@lsst-sqre/squared/src"]
}
Unit tests:
# Run specific test
pnpm test --filter @lsst-sqre/squared -- MyComponent.test.tsx
# Run in watch mode
pnpm test --filter @lsst-sqre/squared -- --watch
Storybook tests:
# Run in watch mode for debugging
pnpm test-storybook:watch --filter @lsst-sqre/squared
# Run specific story test
pnpm test-storybook --filter @lsst-sqre/squared -- --grep "MyComponent"
type over interface for propsReact.FC - type props directly in function parameterssrc/index.tspackages/squared/package.json - Package configurationpackages/squared/vitest.config.ts - Test configuration# Test commands
pnpm test # Unit tests
pnpm test-storybook # Storybook tests
pnpm test-storybook:watch # Watch mode
# Quality commands
pnpm lint # ESLint
pnpm type-check # TypeScript checking
# Storybook commands
pnpm storybook # Dev server
pnpm build-storybook # Build static site
# Utility commands
pnpm clean # Clean caches
Remember: Always run from repository root with --filter @lsst-sqre/squared for proper Turborepo caching!