chore: initial Commit

This commit is contained in:
Hossein Mehrabi 2023-02-02 17:36:26 +03:30
commit 5204ceff92
51 changed files with 9961 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

4
.husky/commit-msg Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no -- commitlint --edit ${1}

4
.husky/pre-commit Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged

3
.prettierignore Normal file
View File

@ -0,0 +1,3 @@
.next
coverage
dist

6
.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"trailingComma": "all"
}

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# Logos Design System
TODO.

View File

@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}

36
apps/lsd-website/.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@ -0,0 +1,38 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View File

@ -0,0 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
}
module.exports = nextConfig

View File

@ -0,0 +1,29 @@
{
"name": "@acid-info/lsd-website",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@acid-info/lsd-react": "*",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@next/font": "13.1.6",
"@types/node": "18.11.18",
"@types/react": "18.0.27",
"@types/react-dom": "18.0.10",
"eslint": "8.33.0",
"eslint-config-next": "13.1.6",
"next": "13.1.6",
"react": "18.2.0",
"react-dom": "18.2.0",
"typescript": "4.9.5"
},
"devDependencies": {
"vite-plugin-dts": "^1.7.2"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="31" fill="none"><g opacity=".9"><path fill="url(#a)" d="M13 .4v29.3H7V6.3h-.2L0 10.5V5L7.2.4H13Z"/><path fill="url(#b)" d="M28.8 30.1c-2.2 0-4-.3-5.7-1-1.7-.8-3-1.8-4-3.1a7.7 7.7 0 0 1-1.4-4.6h6.2c0 .8.3 1.4.7 2 .4.5 1 .9 1.7 1.2.7.3 1.6.4 2.5.4 1 0 1.7-.2 2.5-.5.7-.3 1.3-.8 1.7-1.4.4-.6.6-1.2.6-2s-.2-1.5-.7-2.1c-.4-.6-1-1-1.8-1.4-.8-.4-1.8-.5-2.9-.5h-2.7v-4.6h2.7a6 6 0 0 0 2.5-.5 4 4 0 0 0 1.7-1.3c.4-.6.6-1.3.6-2a3.5 3.5 0 0 0-2-3.3 5.6 5.6 0 0 0-4.5 0 4 4 0 0 0-1.7 1.2c-.4.6-.6 1.2-.6 2h-6c0-1.7.6-3.2 1.5-4.5 1-1.3 2.2-2.3 3.8-3C25 .4 26.8 0 28.8 0s3.8.4 5.3 1.1c1.5.7 2.7 1.7 3.6 3a7.2 7.2 0 0 1 1.2 4.2c0 1.6-.5 3-1.5 4a7 7 0 0 1-4 2.2v.2c2.2.3 3.8 1 5 2.2a6.4 6.4 0 0 1 1.6 4.6c0 1.7-.5 3.1-1.4 4.4a9.7 9.7 0 0 1-4 3.1c-1.7.8-3.7 1.1-5.8 1.1Z"/></g><defs><linearGradient id="a" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient><linearGradient id="b" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient></defs></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>

After

Width:  |  Height:  |  Size: 629 B

View File

@ -0,0 +1,22 @@
import '@/styles/globals.css'
import { dark, light } from '@/theme'
import { Button, ThemeProvider } from '@acid-info/lsd-react'
import type { AppProps } from 'next/app'
import { useState } from 'react'
export default function App({ Component, pageProps }: AppProps) {
const [isDark, setIsDark] = useState(false)
return (
<ThemeProvider theme={isDark ? dark : light}>
<Button
onClick={() => {
setIsDark((v) => !v)
}}
>
switch
</Button>
<Component {...pageProps} />
</ThemeProvider>
)
}

View File

@ -0,0 +1,13 @@
import { Head, Html, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}

View File

@ -0,0 +1,13 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
type Data = {
name: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>,
) {
res.status(200).json({ name: 'John Doe' })
}

View File

@ -0,0 +1,57 @@
import styles from '@/styles/Home.module.css'
import { Button, useTheme } from '@acid-info/lsd-react'
import styled from '@emotion/styled'
import Head from 'next/head'
const StyledButton = styled(Button)`
&.${Button.classes.disabled} {
}
`
export default function Home() {
const theme = useTheme()
return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div style={{ marginBottom: 50 }}>
<Button disabled>Simple Button</Button>
<div
style={{
width: 1000,
marginTop: 50,
display: 'flex',
flexDirection: 'column',
rowGap: 12,
alignItems: 'flex-start',
}}
>
<StyledButton size="small" disabled>
Button
</StyledButton>
<StyledButton disabled size="medium">
Button
</StyledButton>
<StyledButton disabled size="large">
Button
</StyledButton>
<StyledButton size="small">Button</StyledButton>
<StyledButton size="medium">Button</StyledButton>
<StyledButton size="large">Button</StyledButton>
</div>
</div>
<pre>
<code>{theme.globalStyles.styles}</code>
</pre>
<style></style>
</main>
</>
)
}

View File

@ -0,0 +1,279 @@
.main {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
padding: 6rem;
min-height: 100vh;
}
.description {
display: inherit;
justify-content: inherit;
align-items: inherit;
font-size: 0.85rem;
max-width: var(--max-width);
width: 100%;
z-index: 2;
font-family: var(--font-mono);
}
.description a {
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
}
.description p {
position: relative;
margin: 0;
padding: 1rem;
background-color: rgba(var(--callout-rgb), 0.5);
border: 1px solid rgba(var(--callout-border-rgb), 0.3);
border-radius: var(--border-radius);
}
.code {
font-weight: 700;
font-family: var(--font-mono);
}
.grid {
display: grid;
grid-template-columns: repeat(4, minmax(25%, auto));
width: var(--max-width);
max-width: 100%;
}
.card {
padding: 1rem 1.2rem;
border-radius: var(--border-radius);
background: rgba(var(--card-rgb), 0);
border: 1px solid rgba(var(--card-border-rgb), 0);
transition: background 200ms, border 200ms;
}
.card span {
display: inline-block;
transition: transform 200ms;
}
.card h2 {
font-weight: 600;
margin-bottom: 0.7rem;
}
.card p {
margin: 0;
opacity: 0.6;
font-size: 0.9rem;
line-height: 1.5;
max-width: 30ch;
}
.center {
display: flex;
justify-content: center;
align-items: center;
position: relative;
padding: 4rem 0;
}
.center::before {
background: var(--secondary-glow);
border-radius: 50%;
width: 480px;
height: 360px;
margin-left: -400px;
}
.center::after {
background: var(--primary-glow);
width: 240px;
height: 180px;
z-index: -1;
}
.center::before,
.center::after {
content: '';
left: 50%;
position: absolute;
filter: blur(45px);
transform: translateZ(0);
}
.logo,
.thirteen {
position: relative;
}
.thirteen {
display: flex;
justify-content: center;
align-items: center;
width: 75px;
height: 75px;
padding: 25px 10px;
margin-left: 16px;
transform: translateZ(0);
border-radius: var(--border-radius);
overflow: hidden;
box-shadow: 0px 2px 8px -1px #0000001a;
}
.thirteen::before,
.thirteen::after {
content: '';
position: absolute;
z-index: -1;
}
/* Conic Gradient Animation */
.thirteen::before {
animation: 6s rotate linear infinite;
width: 200%;
height: 200%;
background: var(--tile-border);
}
/* Inner Square */
.thirteen::after {
inset: 0;
padding: 1px;
border-radius: var(--border-radius);
background: linear-gradient(
to bottom right,
rgba(var(--tile-start-rgb), 1),
rgba(var(--tile-end-rgb), 1)
);
background-clip: content-box;
}
/* Enable hover only on non-touch devices */
@media (hover: hover) and (pointer: fine) {
.card:hover {
background: rgba(var(--card-rgb), 0.1);
border: 1px solid rgba(var(--card-border-rgb), 0.15);
}
.card:hover span {
transform: translateX(4px);
}
}
@media (prefers-reduced-motion) {
.thirteen::before {
animation: none;
}
.card:hover span {
transform: none;
}
}
/* Mobile */
@media (max-width: 700px) {
.content {
padding: 4rem;
}
.grid {
grid-template-columns: 1fr;
margin-bottom: 120px;
max-width: 320px;
text-align: center;
}
.card {
padding: 1rem 2.5rem;
}
.card h2 {
margin-bottom: 0.5rem;
}
.center {
padding: 8rem 0 6rem;
}
.center::before {
transform: none;
height: 300px;
}
.description {
font-size: 0.8rem;
}
.description a {
padding: 1rem;
}
.description p,
.description div {
display: flex;
justify-content: center;
position: fixed;
width: 100%;
}
.description p {
align-items: center;
inset: 0 0 auto;
padding: 2rem 1rem 1.4rem;
border-radius: 0;
border: none;
border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25);
background: linear-gradient(
to bottom,
rgba(var(--background-start-rgb), 1),
rgba(var(--callout-rgb), 0.5)
);
background-clip: padding-box;
backdrop-filter: blur(24px);
}
.description div {
align-items: flex-end;
pointer-events: none;
inset: auto 0 0;
padding: 2rem;
height: 200px;
background: linear-gradient(
to bottom,
transparent 0%,
rgb(var(--background-end-rgb)) 40%
);
z-index: 1;
}
}
/* Tablet and Smaller Desktop */
@media (min-width: 701px) and (max-width: 1120px) {
.grid {
grid-template-columns: repeat(2, 50%);
}
}
@media (prefers-color-scheme: dark) {
.vercelLogo {
filter: invert(1);
}
.logo,
.thirteen img {
filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70);
}
}
@keyframes rotate {
from {
transform: rotate(360deg);
}
to {
transform: rotate(0deg);
}
}

View File

View File

@ -0,0 +1,33 @@
import { createTheme } from '@acid-info/lsd-react'
export const light = createTheme({
breakpoints: {
xs: {},
sm: {},
lg: {},
xl: {},
},
typography: {},
palette: {
surface: {},
},
})
export const dark = createTheme(
{
breakpoints: {},
typography: {},
palette: {
background: {
primary: 'black',
},
surface: {
primary: 'white',
},
text: {
primary: 'white',
},
},
},
light,
)

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

7
lerna.json Normal file
View File

@ -0,0 +1,7 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"useNx": true,
"useWorkspaces": true,
"version": "0.0.0",
"packages": ["packages/*", "apps/*"]
}

40
package.json Normal file
View File

@ -0,0 +1,40 @@
{
"name": "@acid-info/logos-design-system",
"private": true,
"version": "0.0.0",
"scripts": {
"commit": "git-cz",
"test": "lerna run test",
"format:check": "prettier --config .prettierrc --check --ignore-unknown .",
"format:fix": "prettier --config .prettierrc --write --ignore-unknown -l .",
"prepare": "husky install"
},
"devDependencies": {
"@commitlint/cli": "^17.4.2",
"@commitlint/config-conventional": "^17.4.2",
"@types/node": "^18.11.18",
"commitizen": "^4.3.0",
"cz-conventional-changelog": "^3.3.0",
"husky": "^8.0.3",
"lerna": "^6.4.1",
"lint-staged": "^13.1.0",
"prettier": "^2.8.3"
},
"workspaces": [
"packages/*",
"apps/*"
],
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
"lint-staged": {
"*": "yarn run format:fix"
},
"config": {
"commitizen": {
"path": "node_modules/cz-conventional-changelog"
}
}
}

24
packages/lsd-react/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@ -0,0 +1,46 @@
{
"name": "@acid-info/lsd-react",
"private": true,
"version": "0.0.0",
"type": "module",
"types": "./dist/index.d.ts",
"main": "./dist/lsd-react.js",
"require": "./dist/lsd-react.umd.cjs",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"watch": "tsc && vite build --watch --emptyOutDir false",
"preview": "vite preview"
},
"dependencies": {
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"clsx": "^1.2.1",
"lodash": "^4.17.21",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@babel/plugin-transform-runtime": "^7.19.6",
"@babel/preset-env": "^7.20.2",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.18.6",
"@emotion/babel-plugin": "^11.10.5",
"@rollup/plugin-babel": "^6.0.3",
"@types/lodash": "^4.14.191",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@vitejs/plugin-react": "^3.0.0",
"sass": "^1.58.0",
"typescript": "^4.9.3",
"utility-types": "^3.10.0",
"vite": "^4.0.0",
"vite-plugin-dts": "^1.7.2"
},
"peerDependencies": {
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
}
}

View File

@ -0,0 +1,11 @@
import { LSD_NAMESPACE } from '../Theme/constants'
export const buttonClasses = {
root: `${LSD_NAMESPACE}-button`,
disabled: `${LSD_NAMESPACE}--disabled`,
large: `${LSD_NAMESPACE}--large`,
medium: `${LSD_NAMESPACE}--medium`,
small: `${LSD_NAMESPACE}--small`,
text: `${LSD_NAMESPACE}-button__text`,
}

View File

@ -0,0 +1,47 @@
import { css } from '@emotion/react'
import { withTheme } from '../Theme/withTheme'
import { buttonClasses } from './Button.classes'
export const ButtonStyles = withTheme(
(theme) => css`
.${buttonClasses.root} {
width: auto;
color: var(--lsd-text-primary);
background: none;
border: 1px solid var(--lsd-surface-primary);
cursor: pointer;
padding: 6px 24px;
@media (max-width: ${theme.breakpoints.lg.width}px) {
color: red;
border-color: red;
}
}
.${buttonClasses.disabled} {
cursor: default;
color: var(--lsd-surface-disabled);
border-color: var(--lsd-surface-disabled);
}
.${buttonClasses.large} {
padding: 10px 40px;
}
.${buttonClasses.medium} {
}
.${buttonClasses.small} {
padding: 6px 12px;
}
.${buttonClasses.root}:hover {
&:not(.${buttonClasses.disabled}) {
.${buttonClasses.text} {
text-decoration: underline;
}
}
}
`,
)

View File

@ -0,0 +1,29 @@
import clsx from 'clsx'
import React from 'react'
import { buttonClasses } from './Button.classes'
export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
size?: 'large' | 'medium' | 'small'
}
export const Button: React.FC<ButtonProps> & {
classes: typeof buttonClasses
} = ({ size = 'medium', children, ...props }) => {
return (
<>
<button
{...props}
className={clsx(
props.className,
buttonClasses.root,
buttonClasses[size],
props.disabled && buttonClasses.disabled,
)}
>
<span className={buttonClasses.text}>{children}</span>
</button>
</>
)
}
Button.classes = buttonClasses

View File

@ -0,0 +1 @@
export * from './Button'

View File

@ -0,0 +1,19 @@
import { Global } from '@emotion/react'
import React, { useMemo } from 'react'
import { ButtonStyles } from '../Button/Button.styles'
import { Theme } from '../Theme'
import { defaultTheme } from '../Theme/defaultTheme'
export const CSSBaseline: React.FC<{ theme?: Theme }> = ({
theme = defaultTheme,
}) => {
const styles = useMemo(
() =>
[ButtonStyles]
.map((style) => (typeof style === 'function' ? style(theme) : style))
.map((style) => <Global key={style.name} styles={style} />),
[theme],
)
return <>{styles}</>
}

View File

@ -0,0 +1 @@
export * from './CSSBaseline'

View File

@ -0,0 +1,25 @@
import { Global, ThemeProvider as EmotionThemeProvider } from '@emotion/react'
import React from 'react'
import { CSSBaseline } from '../CSSBaseline'
import { Theme } from './types'
export type ThemeProviderProps = React.PropsWithChildren<{
theme: Theme
}>
export const ThemeProvider: React.FC<ThemeProviderProps> = ({
theme,
children,
}) => {
return (
<ThemeContext.Provider value={{ theme }}>
<EmotionThemeProvider theme={theme}>{children}</EmotionThemeProvider>
<CSSBaseline theme={theme} />
<Global styles={theme.globalStyles} />
</ThemeContext.Provider>
)
}
export const ThemeContext = React.createContext({
theme: null as any,
})

View File

@ -0,0 +1,35 @@
import {
Breakpoints,
TypographyProperties,
TypographyVariants,
VariantThemeProperties,
} from './types'
export const LSD_NAMESPACE = 'lsd'
export const THEME_BREAKPOINTS = ['xs', 'sm', 'md', 'lg', 'xl'] as Breakpoints[]
export const THEME_TYPOGRAPHY_VARIANTS = [
'headlineLg',
'headlineStd',
'headlineMd',
'headlineSm',
'titleLg',
'titleMd',
'titleSm',
'bodySm',
'bodyMd',
'bodyLg',
'labelLg',
'labelMd',
'labelSm',
] as TypographyVariants[]
export const THEME_TYPOGRAPHY_PROPERTIES = [
'fontSize',
'lineHeight',
] as TypographyProperties[]
export const THEME_VARIANT_PROPERTIES = [
'typography',
] as VariantThemeProperties[]

View File

@ -0,0 +1,88 @@
import { css } from '@emotion/react'
import defaultsDeep from 'lodash/defaultsDeep'
import { pairs } from '../../utils/object.utils'
import { THEME_BREAKPOINTS, THEME_TYPOGRAPHY_VARIANTS } from './constants'
import { defaultTheme } from './defaultTheme'
import { createThemeGlobalStyles } from './globalStyles'
import type {
BreakpointStyles,
CreateThemeProps,
Theme,
ThemeBreakpoints,
TypographyVariants,
VariantThemeProperties,
} from './types'
const createTypographyStyles = (theme: CreateThemeProps, defaultTheme: Theme) =>
pairs<TypographyVariants>(THEME_TYPOGRAPHY_VARIANTS, (variant) => ({
...defaultTheme.typography[variant],
...(theme.typography[variant] ?? {}),
}))
const createBreakpointStyle = (
key: VariantThemeProperties,
all: BreakpointStyles[],
index: number,
theme: CreateThemeProps,
defaultTheme: Theme,
) => {
switch (key) {
case 'typography':
return pairs<TypographyVariants>(defaultTheme[key], (variant) => ({
...defaultTheme[key][variant],
...theme[key][variant],
...(all?.[index - 1]?.[key]?.[variant] ?? {}),
...(theme.breakpoints?.[THEME_BREAKPOINTS[index]]?.[key]?.[variant] ??
{}),
}))
default:
return {}
}
}
const createBreakpointStyles = (
theme: CreateThemeProps,
defaultTheme: Theme,
): Theme['breakpoints'] =>
Object.fromEntries(
THEME_BREAKPOINTS.reduce<BreakpointStyles[]>(
(all, key, index) =>
[
...all,
{
...defaultTheme.breakpoints[key],
...theme.breakpoints[key],
...pairs(['typography'], (key) =>
createBreakpointStyle(
key as any,
all,
index,
theme,
defaultTheme,
),
),
},
] as BreakpointStyles[],
[],
).map((styles, index) => [THEME_BREAKPOINTS[index], styles]),
) as ThemeBreakpoints
const createPaletteStyles = (theme: CreateThemeProps, defaultTheme: Theme) =>
defaultsDeep(theme.palette, defaultTheme.palette)
export const createTheme = (
props: CreateThemeProps,
from = defaultTheme,
): Theme => {
const theme: Theme = {
typography: createTypographyStyles(props, from),
breakpoints: createBreakpointStyles(props, from),
palette: createPaletteStyles(props, from),
globalStyles: css``,
}
theme.globalStyles = createThemeGlobalStyles(theme)
return theme
}

View File

@ -0,0 +1,182 @@
import { css } from '@emotion/react'
import { createThemeGlobalStyles } from './globalStyles'
import { Theme } from './types'
export const defaultTheme: Theme = {
breakpoints: {
xs: {
width: 0,
typography: {
headlineLg: {},
headlineMd: {},
headlineStd: {},
headlineSm: {},
titleLg: {},
titleMd: {},
titleSm: {},
bodyLg: {},
bodyMd: {},
bodySm: {},
labelLg: {},
labelMd: {},
labelSm: {},
},
},
sm: {
width: 400,
typography: {
headlineLg: {},
headlineMd: {},
headlineStd: {},
headlineSm: {},
titleLg: {},
titleMd: {},
titleSm: {},
bodyLg: {},
bodyMd: {},
bodySm: {},
labelLg: {},
labelMd: {},
labelSm: {},
},
},
md: {
width: 768,
typography: {
headlineLg: {},
headlineMd: {},
headlineStd: {},
headlineSm: {},
titleLg: {},
titleMd: {},
titleSm: {},
bodyLg: {},
bodyMd: {},
bodySm: {},
labelLg: {},
labelMd: {},
labelSm: {},
},
},
lg: {
width: 1024,
typography: {
headlineLg: {},
headlineMd: {},
headlineStd: {},
headlineSm: {},
titleLg: {},
titleMd: {},
titleSm: {},
bodyLg: {},
bodyMd: {},
bodySm: {},
labelLg: {},
labelMd: {},
labelSm: {},
},
},
xl: {
width: 1200,
typography: {
headlineLg: {},
headlineMd: {},
headlineStd: {},
headlineSm: {},
titleLg: {},
titleMd: {},
titleSm: {},
bodyLg: {},
bodyMd: {},
bodySm: {},
labelLg: {},
labelMd: {},
labelSm: {},
},
},
},
typography: {
headlineLg: {
fontSize: '2.875rem',
lineHeight: '3.25rem',
},
headlineMd: {
fontSize: '1.75rem',
lineHeight: '2.25rem',
},
headlineStd: {
fontSize: '2rem',
lineHeight: '2.5rem',
},
headlineSm: {
fontSize: '1.5rem',
lineHeight: '2rem',
},
titleLg: {
fontSize: '1.375rem',
lineHeight: '1.75rem',
},
titleMd: {
fontSize: '1rem',
lineHeight: '1.5rem',
},
titleSm: {
fontSize: '0.875rem',
lineHeight: '1.25rem',
},
bodyLg: {
fontSize: '1rem',
lineHeight: '1.5rem',
},
bodyMd: {
fontSize: '0.875rem',
lineHeight: '1.25rem',
},
bodySm: {
fontSize: '0.75rem',
lineHeight: '1rem',
},
labelLg: {
fontSize: '0.875rem',
lineHeight: '1.25rem',
},
labelMd: {
fontSize: '0.875rem',
lineHeight: '1.25rem',
},
labelSm: {
fontSize: '0.75rem',
lineHeight: '1rem',
},
},
palette: {
background: {
primary: 'rgba(255, 255, 255, 1)',
secondary: 'rgba(0, 0, 0, 1)',
},
border: {
primary: 'rgba(51, 51, 56, 1)',
secondary: 'rgba(255, 255, 255, 1)',
tertiary: 'rgba(223, 223, 226, 1)',
},
surface: {
primary: 'rgba(0, 0, 0, 1)',
secondary: 'rgba(0, 0, 0, 0.34)',
tertiary: 'rgba(0, 0, 0, 0.2)',
disabled: 'rgba(168, 168, 168, 1)',
},
text: {
primary: 'rgba(0, 0, 0, 1)',
secondary: 'rgba(0, 0, 0, 0.34)',
placeholder: 'rgba(0, 0, 0, 0.34)',
disabled: 'rgba(0, 0, 0, 0.34)',
},
icons: {
primary: 'rgba(0, 0, 0, 1)',
disabled: 'rgba(0, 0, 0, 0.34)',
},
},
globalStyles: css``,
}
defaultTheme.globalStyles = createThemeGlobalStyles(defaultTheme)

View File

@ -0,0 +1,131 @@
import { css } from '@emotion/react'
import {
LSD_NAMESPACE,
THEME_BREAKPOINTS,
THEME_TYPOGRAPHY_PROPERTIES,
THEME_TYPOGRAPHY_VARIANTS,
} from './constants'
import { Breakpoints, Theme, TypographyVariants } from './types'
export const gs = {
breakpoint: (
theme: Theme,
breakpoint: Breakpoints,
content: string,
) => `@media (min-width: ${theme.breakpoints[breakpoint].width}px) {
${content}
}`,
vars: {
name: (...parts: string[]) => `--${[LSD_NAMESPACE, ...parts].join('-')}`,
wrap: (v: string, wrap = true) => (!wrap ? v : `var(${v})`),
define: (name: string, value: any) => `${name}: ${value};`,
typography: (
variant: TypographyVariants,
property: string,
breakpoint?: Breakpoints | 'default',
) => gs.vars.name(variant, property, ...(breakpoint ? [breakpoint] : [])),
palette: (category: string, variant: string) =>
gs.vars.name(category, variant),
},
all: (theme: Theme) =>
[gs.typography.all(theme), gs.palette.all(theme)].join('\n'),
typography: {
all: (theme: Theme) => [gs.typography.variants(theme)].join('\n'),
variants: (theme: Theme) =>
[
...THEME_TYPOGRAPHY_VARIANTS.flatMap((variant) =>
THEME_TYPOGRAPHY_PROPERTIES.map((prop) =>
gs.vars.define(
gs.vars.typography(variant, prop),
theme.typography[variant][prop],
),
),
),
gs.typography.breakpoints(theme),
].join('\n'),
breakpoints: (theme: Theme) =>
THEME_BREAKPOINTS.map((breakpoint, index) =>
gs.breakpoint(
theme,
breakpoint,
gs.typography.breakpoint(theme, breakpoint, index),
),
).join('\n'),
breakpoint: (
theme: Theme,
breakpoint: Breakpoints,
breakpointIndex: number,
) =>
THEME_TYPOGRAPHY_VARIANTS.flatMap((variant) =>
THEME_TYPOGRAPHY_PROPERTIES.map((prop) => {
const value = theme.breakpoints[breakpoint].typography[variant][prop]
const current =
breakpointIndex > 0
? theme.breakpoints?.[THEME_BREAKPOINTS[breakpointIndex - 1]]
?.typography?.[variant]?.[prop]
: theme.typography[variant][prop]
return value !== current
? gs.vars.define(gs.vars.typography(variant, prop), value)
: undefined
}),
)
.filter((value) => !!value)
.join('\n'),
},
palette: {
all: (theme: Theme) => {
const palette = theme.palette as Record<string, Record<string, string>>
return [
...Object.keys(palette).flatMap((name) =>
Object.keys(palette[name]).map((variant) =>
gs.palette.color(name, variant, palette[name][variant]),
),
),
`:root {
html, body {
background-color: ${theme.palette.background.primary};
}
}
`,
].join('\n')
},
color: (name: string, variant: string, value: string) =>
gs.vars.define(gs.vars.palette(name, variant), value),
},
}
export const createThemeGlobalStyles = (() => {
return (theme: Theme) => {
const cache: any = {}
const key = theme as any as string
if (
cache[key] &&
process.env.NODE_ENV === 'production' &&
typeof window !== 'undefined'
)
return cache[key]
const styles = globalStyles.all(theme)
cache[key] = css`
:root {
${styles}
}
`
return cache[key]
}
})()
export const globalStyles = gs

View File

@ -0,0 +1,13 @@
export {
LSD_NAMESPACE,
THEME_BREAKPOINTS,
THEME_TYPOGRAPHY_PROPERTIES,
THEME_TYPOGRAPHY_VARIANTS,
THEME_VARIANT_PROPERTIES,
} from './constants'
export { createTheme } from './createTheme'
export { createThemeGlobalStyles, globalStyles } from './globalStyles'
export { ThemeProvider, type ThemeProviderProps } from './ThemeProvider'
export * from './types'
export { useTheme } from './useTheme'
export { withTheme } from './withTheme'

View File

@ -0,0 +1,96 @@
import { SerializedStyles } from '@emotion/react'
import { CSSProperties } from 'react'
import { DeepPartial } from 'utility-types'
export type Breakpoints = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
export type TypographyTypefaces = 'sans-serif' | 'serif' | 'mono'
export type TypographyVariants =
| 'headlineLg'
| 'headlineStd'
| 'headlineMd'
| 'headlineSm'
| 'titleLg'
| 'titleMd'
| 'titleSm'
| 'bodyLg'
| 'bodyMd'
| 'bodySm'
| 'labelLg'
| 'labelMd'
| 'labelSm'
export type VariantThemeProperties = keyof Pick<Theme, 'typography'>
export type TypographyStyles = Pick<CSSProperties, 'fontSize' | 'lineHeight'>
export type TypographyProperties = keyof TypographyStyles
export type ThemeTypography<T extends string = TypographyVariants> = {
[key in T]: TypographyStyles
}
export type ThemePalette = {
background: {
primary: string
secondary: string
}
border: {
primary: string
secondary: string
tertiary: string
}
surface: {
primary: string
secondary: string
tertiary: string
disabled: string
}
text: {
primary: string
secondary: string
placeholder: string
disabled: string
}
icons: {
primary: string
disabled: string
}
}
export type BreakpointStyles = {
width: number
} & Pick<Theme, 'typography'>
export type ThemeBreakpoints = {
[key in Breakpoints]: BreakpointStyles
}
export type Theme = {
breakpoints: ThemeBreakpoints
typography: ThemeTypography
palette: ThemePalette
globalStyles: SerializedStyles
}
export type ThemeOptionBreakpointStyles = Partial<
Omit<BreakpointStyles, 'width' | 'typography'> & {
width?: number
typography: ThemeOptionTypography
}
>
export type ThemeOptionBreakpoints = {
[K in keyof ThemeBreakpoints]?: ThemeOptionBreakpointStyles
}
export type ThemeOptionTypographyStyle = TypographyStyles
export type ThemeOptionTypography = DeepPartial<ThemeTypography>
export type ThemeOptionPalette = DeepPartial<ThemePalette>
export type CreateThemeProps = {
breakpoints: ThemeOptionBreakpoints
typography: ThemeOptionTypography
palette: ThemeOptionPalette
}
export type ThemeContext = {
theme: Theme
}

View File

@ -0,0 +1,10 @@
import { useContext } from 'react'
import { defaultTheme } from './defaultTheme'
import { ThemeContext } from './ThemeProvider'
import type { Theme } from './types'
export const useTheme = <T extends Theme>() => {
const theme = useContext(ThemeContext).theme as T
return theme ?? defaultTheme
}

View File

@ -0,0 +1,6 @@
import { SerializedStyles } from '@emotion/react'
import { Theme } from './types'
export const withTheme =
(style: (theme: Theme) => SerializedStyles) => (theme: Theme) =>
style(theme)

View File

@ -0,0 +1,2 @@
export * from './components/Button'
export * from './components/Theme'

View File

@ -0,0 +1,10 @@
export const pairs = <K extends string = string, T = any>(
obj: string[] | Object,
map: (key: K, index: number) => T,
): Record<K, T> =>
Object.fromEntries(
(Array.isArray(obj) ? obj : Object.keys(obj)).map((key, index) => [
key,
map(key, index),
]),
)

1
packages/lsd-react/src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"declaration": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true
},
"include": ["vite.config.ts"]
}

View File

@ -0,0 +1,38 @@
import react from '@vitejs/plugin-react'
import path from 'path'
import { defineConfig } from 'vite'
import dts from 'vite-plugin-dts'
export default defineConfig({
plugins: [
react({
jsxImportSource: '@emotion/react',
babel: {
plugins: ['@emotion/babel-plugin'],
},
}),
dts({
include: ['src'],
}),
],
build: {
minify: false,
outDir: 'dist',
lib: {
entry: path.resolve(__dirname, './src/index.ts'),
name: 'lsd-react',
fileName: 'lsd-react',
},
rollupOptions: {
external: ['react', 'react-dom', '@emotion/styled', '@emotion/react'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
},
},
},
})

20
tsconfig/base.json Normal file
View File

@ -0,0 +1,20 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Default",
"compilerOptions": {
"composite": false,
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"inlineSources": false,
"isolatedModules": true,
"moduleResolution": "node",
"noUnusedLocals": false,
"noUnusedParameters": false,
"preserveWatchOutput": true,
"skipLibCheck": true,
"strict": true
},
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,11 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "React Library",
"extends": "./base.json",
"compilerOptions": {
"jsx": "react-jsx",
"lib": ["ES2015"],
"module": "ESNext",
"target": "es6"
}
}

8459
yarn.lock Normal file

File diff suppressed because it is too large Load Diff