diff --git a/.eslintignore b/.eslintignore index 36080e41..e18ab254 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,6 +5,7 @@ /docs/pages/playground/ /packages/pigment-css-react/utils/ /packages/pigment-css-react/processors/ +/packages/pigment-css-react/internal/ /packages/pigment-css-react/exports/ /packages/pigment-css-react/theme/ /packages/pigment-css-react/tests/**/fixtures diff --git a/.eslintrc.js b/.eslintrc.js index fe499326..d36b0c24 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -43,6 +43,7 @@ module.exports = { */ rules: { 'consistent-this': ['error', 'self'], + 'import/prefer-default-export': 'off', curly: ['error', 'all'], // Just as bad as "max components per file" 'max-classes-per-file': 'off', diff --git a/.gitignore b/.gitignore index 5acd48e2..f59a6567 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ !.vscode/extensions.json *.log *.tsbuildinfo -/.eslintcache +.eslintcache /.nyc_output /benchmark/**/dist /coverage diff --git a/docs/.eslintrc.js b/docs/.eslintrc.js new file mode 100644 index 00000000..959aad4c --- /dev/null +++ b/docs/.eslintrc.js @@ -0,0 +1,18 @@ +module.exports = { + rules: { + 'react/prop-types': 'off', + 'react/react-in-jsx-scope': 'off', + 'react/no-unknown-property': ['error', { ignore: ['sx'] }], + 'import/extensions': [ + 'error', + 'ignorePackages', + { + '': 'never', + js: 'never', + jsx: 'never', + ts: 'never', + tsx: 'never', + }, + ], + }, +}; diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..26b002aa --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,40 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# env files (can opt-in for commiting if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..f717cd73 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,4 @@ +# Pigment CSS Docs app + +This is the Pigment CSS docs application bootstrapped with Next.js 15. It uses app router and +Pigment CSS for styling. diff --git a/docs/data/getMarkdownPage.ts b/docs/data/getMarkdownPage.ts new file mode 100644 index 00000000..8a38dd0c --- /dev/null +++ b/docs/data/getMarkdownPage.ts @@ -0,0 +1,87 @@ +import * as path from 'path'; +import * as fs from 'node:fs/promises'; +import { evaluate } from '@mdx-js/mdx'; +import * as jsxRuntime from 'react/jsx-runtime'; +import remarkFrontmatter from 'remark-frontmatter'; +import remarkMdxFrontmatter from 'remark-mdx-frontmatter'; +import remarkGfm from 'remark-gfm'; +import rehypeSlug from 'rehype-slug'; +import extractToc, { type Toc } from '@stefanprobst/rehype-extract-toc'; +import exportToc from '@stefanprobst/rehype-extract-toc/mdx'; +import { read as readVFile } from 'to-vfile'; +import { matter } from 'vfile-matter'; + +export interface PageMetadata { + title: string; + description: string; + components?: string; + githubLabel?: string; + slug: string; +} + +async function getFileHandle(basePath: string, slug: string) { + const mdxFilePath = path.join(process.env.DATA_DIR as string, basePath, slug, `${slug}.mdx`); + const mdFilePath = path.join(process.env.DATA_DIR as string, basePath, slug, `${slug}.md`); + + try { + const fileHandle = await fs.open(mdxFilePath); + return { + handle: fileHandle, + path: mdxFilePath, + [Symbol.asyncDispose]: async () => { + await fileHandle.close(); + }, + }; + } catch (ex1) { + try { + const fileHandle = await fs.open(mdFilePath); + return { + handle: fileHandle, + path: mdFilePath, + [Symbol.asyncDispose]: async () => { + await fileHandle.close(); + }, + }; + } catch (ex2) { + throw new Error('404'); + } + } +} + +export async function getMarkdownPage(basePath: string, slug: string) { + await using file = await getFileHandle(basePath, slug); + const mdxSource = await file.handle.readFile({ + encoding: 'utf-8', + }); + const { + default: MDXContent, + frontmatter, + tableOfContents, + } = await evaluate(mdxSource, { + ...jsxRuntime, + remarkPlugins: [remarkGfm, remarkFrontmatter, remarkMdxFrontmatter], + rehypePlugins: [ + // [rehypePrettyCode, { theme: config.shikiThemes }], + rehypeSlug, + extractToc, + exportToc, + ], + }); + + return { + metadata: { + ...(frontmatter as Partial), + slug, + } as PageMetadata, + tableOfContents: tableOfContents as Toc, + MDXContent, + }; +} + +export async function getMarkdownPageMetadata(basePath: string, slug: string) { + await using file = await getFileHandle(basePath, slug); + + const vfile = await readVFile(file.path); + matter(vfile); + return vfile.data.matter as PageMetadata; +} diff --git a/docs/data/getting-started/overview/overview.mdx b/docs/data/getting-started/overview/overview.mdx new file mode 100644 index 00000000..3da139a9 --- /dev/null +++ b/docs/data/getting-started/overview/overview.mdx @@ -0,0 +1,12 @@ +--- +title: Quick start +description: Get started with Pigment CSS, a zero-runtime CSS-in-JS library. +--- + +# Quick start + + + +## Installation + +Pigment CSS has two category of packages. First one is to be imported and used in your source code. This includes the public API of the package. Second category is to be imported in your bundler config file to configure and actually be able to use Pigment CSS. We currently support [`Next.js`](https://nextjs.org/) (no Turbopack yet), [`Vite`](https://vite.dev/) and [`Webpack`](https://webpack.js.org/). diff --git a/docs/data/pages.ts b/docs/data/pages.ts new file mode 100644 index 00000000..8b8a1435 --- /dev/null +++ b/docs/data/pages.ts @@ -0,0 +1,36 @@ +export interface RouteMetadata { + pathname: string; + title?: string; + children?: readonly RouteMetadata[]; + planned?: boolean; + unstable?: boolean; +} + +const pages: readonly RouteMetadata[] = [ + { + pathname: '/getting-started', + title: 'Getting started', + children: [{ pathname: '/getting-started/overview', title: 'Overview' }], + }, +]; + +export default pages; + +function extractSlug(pathname: string) { + return pathname.split('/').pop()!; +} + +export function getSlugs(parentPath: string) { + const slugs: string[] = []; + + const categoryPages = pages.find((page) => page.pathname === parentPath); + categoryPages?.children?.forEach((level2Page) => { + if (level2Page.children) { + slugs.push(...level2Page.children.map((page) => extractSlug(page.pathname))); + } else { + slugs.push(extractSlug(level2Page.pathname)); + } + }); + + return slugs; +} diff --git a/docs/next.config.ts b/docs/next.config.ts new file mode 100644 index 00000000..edca3bd0 --- /dev/null +++ b/docs/next.config.ts @@ -0,0 +1,33 @@ +import type { NextConfig } from 'next'; +import { withPigment, extendTheme } from '@pigment-css/nextjs-plugin'; +import path from 'path'; +import { theme as baseTheme } from './src/theme'; + +const DATA_DIR = path.join(process.cwd(), 'data'); + +const nextConfig: NextConfig = { + trailingSlash: false, + env: { + DATA_DIR, + }, + distDir: 'export', + output: process.env.NODE_ENV === 'production' ? 'export' : undefined, + eslint: { + ignoreDuringBuilds: true, + }, +}; + +const theme = extendTheme({ + colorSchemes: { + light: baseTheme, + }, +}); + +export default withPigment(nextConfig, { + theme, + displayName: true, + sourceMap: true, + babelOptions: { + plugins: ['@babel/plugin-proposal-explicit-resource-management'], + }, +}); diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000..9b4c3bfa --- /dev/null +++ b/docs/package.json @@ -0,0 +1,54 @@ +{ + "name": "docs", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "cross-env NODE_ENV=production next build", + "preview": "serve ./export", + "lint": "next lint", + "typescript": "tsc --noEmit -p ." + }, + "dependencies": { + "@base_ui/react": "^1.0.0-alpha.3", + "@mdx-js/mdx": "^3.1.0", + "@pigment-css/react": "workspace:*", + "@stefanprobst/rehype-extract-toc": "^2.2.0", + "clsx": "^2.1.1", + "react": "18.3.1", + "react-dom": "18.3.1", + "next": "15.0.2", + "rehype-slug": "^6.0.0", + "remark-frontmatter": "^5.0.0", + "remark-gfm": "^4.0.0", + "remark-mdx-frontmatter": "^5.0.0", + "to-vfile": "^8.0.0", + "vfile-matter": "^5.0.0" + }, + "devDependencies": { + "@babel/plugin-proposal-explicit-resource-management": "^7.25.9", + "@pigment-css/nextjs-plugin": "workspace:*", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "eslint-config-next": "15.0.2", + "serve": "14.2.4", + "tailwindcss": "^3.4.14" + }, + "nx": { + "targets": { + "typescript": { + "cache": false, + "dependsOn": [ + "^build" + ] + }, + "build": { + "outputs": [ + "{projectRoot}/.next", + "{projectRoot}/export" + ] + } + } + } +} diff --git a/docs/src/app/(content)/getting-started/[slug]/not-found.tsx b/docs/src/app/(content)/getting-started/[slug]/not-found.tsx new file mode 100644 index 00000000..51610464 --- /dev/null +++ b/docs/src/app/(content)/getting-started/[slug]/not-found.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; +import { MainContent } from '@/components/MainContent'; + +export default function NotFoundPage() { + return ( + +

Not Found

+

The page that you were looking for could not be found.

+
+ ); +} diff --git a/docs/src/app/(content)/getting-started/[slug]/page.tsx b/docs/src/app/(content)/getting-started/[slug]/page.tsx new file mode 100644 index 00000000..90b4d6cd --- /dev/null +++ b/docs/src/app/(content)/getting-started/[slug]/page.tsx @@ -0,0 +1,79 @@ +import * as React from 'react'; +import { notFound } from 'next/navigation'; +import { getSlugs } from '@data/pages'; +import { Metadata } from 'next'; +import { getMarkdownPage, getMarkdownPageMetadata } from '@data/getMarkdownPage'; +import { TableOfContents } from '@/components/TableOfContents'; +import { Description } from '@/components/mdx/Description'; +import { components } from '@/components/mdx/MDXComponents'; +import { MainContent } from '@/components/MainContent'; + +interface Props { + params: Promise<{ slug: string }>; +} + +const SEGMENT = 'getting-started'; + +export default async function GettingStartedPage(props: Props) { + const { slug } = await props.params; + try { + const { MDXContent, metadata, tableOfContents } = await getMarkdownPage(SEGMENT, slug); + const allComponents = { + ...components, + Description: () => , + Demo: () => null, + }; + + return ( + + + + {/*
+
+ +
+
+ +
+
*/} +
+ + +
+ ); + } catch (ex) { + if ((ex as Error).message === '404') { + return notFound(); + } + throw ex; + } +} + +export function generateStaticParams() { + return getSlugs(`/${SEGMENT}`).map((slug) => ({ slug })); +} + +export async function generateMetadata({ params }: Props): Promise { + const { slug } = await params; + const { title = 'Getting started', description } = await getMarkdownPageMetadata(SEGMENT, slug); + + return { + title: { + absolute: title, + template: '%s | Pigment CSS', + }, + description, + twitter: { + title, + description, + }, + openGraph: { + title, + description, + }, + }; +} diff --git a/docs/src/app/(content)/layout.tsx b/docs/src/app/(content)/layout.tsx new file mode 100644 index 00000000..388864a8 --- /dev/null +++ b/docs/src/app/(content)/layout.tsx @@ -0,0 +1,15 @@ +import routes from '@data/pages'; +import { AppBar } from '@/components/AppBar'; +import { Navigation } from '@/components/Navigation'; + +export default function ContentLayout({ children }: React.PropsWithChildren<{}>) { + return ( +
+ +
+ + {children} +
+
+ ); +} diff --git a/docs/src/app/favicon.ico b/docs/src/app/favicon.ico new file mode 100644 index 00000000..718d6fea Binary files /dev/null and b/docs/src/app/favicon.ico differ diff --git a/docs/src/app/fonts/GeistMonoVF.woff b/docs/src/app/fonts/GeistMonoVF.woff new file mode 100644 index 00000000..f2ae185c Binary files /dev/null and b/docs/src/app/fonts/GeistMonoVF.woff differ diff --git a/docs/src/app/fonts/GeistVF.woff b/docs/src/app/fonts/GeistVF.woff new file mode 100644 index 00000000..1b62daac Binary files /dev/null and b/docs/src/app/fonts/GeistVF.woff differ diff --git a/docs/src/app/globals.css b/docs/src/app/globals.css new file mode 100644 index 00000000..9efb844b --- /dev/null +++ b/docs/src/app/globals.css @@ -0,0 +1,35 @@ +html, +body { + max-width: 100vw; + overflow-x: hidden; +} + +::selection { + background: var(--gray-container-3); +} + +body { + font-family: Arial, Helvetica, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + padding-top: 49px; +} + +@layer global-reset { + * { + box-sizing: border-box; + padding: 0; + margin: 0; + } + + a { + color: inherit; + text-decoration: none; + } +} +/* +@media (prefers-color-scheme: dark) { + html { + color-scheme: dark; + } +} */ diff --git a/docs/src/app/layout.tsx b/docs/src/app/layout.tsx new file mode 100644 index 00000000..7e54c490 --- /dev/null +++ b/docs/src/app/layout.tsx @@ -0,0 +1,33 @@ +import type { Metadata } from 'next'; +import localFont from 'next/font/local'; +import '@pigment-css/react/theme'; +import '@pigment-css/react/styles.css'; +import './globals.css'; + +const geistSans = localFont({ + src: './fonts/GeistVF.woff', + variable: '--font-geist-sans', + weight: '100 900', +}); +const geistMono = localFont({ + src: './fonts/GeistMonoVF.woff', + variable: '--font-geist-mono', + weight: '100 900', +}); + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + + ); +} diff --git a/docs/src/app/page.tsx b/docs/src/app/page.tsx new file mode 100644 index 00000000..b139e2e9 --- /dev/null +++ b/docs/src/app/page.tsx @@ -0,0 +1,84 @@ +import { styled } from '@pigment-css/react'; +import { Metadata } from 'next'; +import Link from 'next/link'; + +const Page = styled.div` + display: grid; + grid-template-rows: 20px 1fr 20px; + place-items: center center; + min-height: 100svh; + padding: 80px; + gap: 64px; + font-family: var(--font-geist-sans); + + @media (prefers-color-scheme: dark) { + & { + --gray-rgb: 255, 255, 255; + --gray-alpha-200: rgb(var(--gray-rgb) 0.145); + --gray-alpha-100: rgb(var(--gray-rgb) 0.06); + --button-primary-hover: #ccc; + --button-secondary-hover: #1a1a1a; + } + } + + @media (max-width: 600px) { + & { + padding: 32px; + padding-bottom: 80px; + } + } +`; + +const Main = styled.main` + display: flex; + flex-direction: column; + gap: 32px; + grid-row-start: 2; + + & ol { + font-family: var(--font-geist-mono); + padding-left: 0; + margin: 0; + font-size: 14px; + line-height: 24px; + letter-spacing: -0.01em; + list-style-position: inside; + } + + & li:not(:last-of-type) { + margin-bottom: 8px; + } + + & code { + font-family: inherit; + background: var(--gray-alpha-100); + padding: 2px 4px; + border-radius: 4px; + font-weight: 600; + } + + @media (max-width: 600px) { + align-items: center; + + & ol { + text-align: center; + } + } +`; + +export default function Home() { + return ( + +
+ Get Started +
+
+ ); +} + +export const metadata: Metadata = { + title: { + absolute: 'Pigment CSS', + template: '%s | Pigment CSS', + }, +}; diff --git a/docs/src/augment.d.ts b/docs/src/augment.d.ts new file mode 100644 index 00000000..855c08bb --- /dev/null +++ b/docs/src/augment.d.ts @@ -0,0 +1,16 @@ +import type { SxProp } from '@pigment-css/react'; +import { Theme } from './theme'; + +declare module '@pigment-css/react/theme' { + export interface ThemeArgs { + theme: Theme & { + vars: Theme; + }; + } +} + +declare module 'react' { + interface Attributes { + sx?: SxProp | undefined; + } +} diff --git a/docs/src/components/AppBar.tsx b/docs/src/components/AppBar.tsx new file mode 100644 index 00000000..9568a8a1 --- /dev/null +++ b/docs/src/components/AppBar.tsx @@ -0,0 +1,71 @@ +import * as React from 'react'; +import { styled } from '@pigment-css/react'; +import packageJson from '../../../package.json'; +import { IconLinkButton } from './IconLinkButton'; +import { IconButton } from './IconButton'; +import { DocsVersionSelector } from './DocsVersionSelector'; +import { PigmentIcon } from './icons/Pigment'; +import { GitHubIcon } from './icons/Github'; +import { SettingsIcon } from './icons/Settings'; + +const currentVersion = packageJson.version; +const supportedVersions = [ + { + version: currentVersion, + url: '#', + }, +]; + +const Header = styled.header(({ theme }) => ({ + boxSizing: 'border-box', + position: 'fixed', + top: '0px', + width: '100%', + zIndex: 1, + backgroundColor: 'rgba(255, 255, 255, 0.5)', + backgroundClip: 'padding-box', + backdropFilter: 'blur(4.25px) saturate(125%)', + padding: `${theme.vars.space[1]} ${theme.vars.space[3]}`, + borderBottom: `1px solid ${theme.vars.gray.outline[1]}`, + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', +})); + +export function AppBar() { + return ( +
+
({ + display: 'flex', + alignItems: 'center', + gap: theme.vars.space[2], + })} + > + + + + +
+
+ + + + + + +
+
+ ); +} diff --git a/docs/src/components/DocsVersionSelector.tsx b/docs/src/components/DocsVersionSelector.tsx new file mode 100644 index 00000000..2f8f0da0 --- /dev/null +++ b/docs/src/components/DocsVersionSelector.tsx @@ -0,0 +1,123 @@ +'use client'; +import * as React from 'react'; +import * as Menu from '@base_ui/react/Menu'; +import { styled } from '@pigment-css/react'; +import { SelectIcon } from './icons/Select'; + +export interface DocumentationVersion { + version: string; + url: string; +} + +export interface DocsVersionSelectorProps { + versions: DocumentationVersion[]; + currentVersion: string; +} + +const Trigger = styled(Menu.Trigger)(({ theme }) => ({ + all: 'unset', + position: 'relative', + lineHeight: 1, + verticalAlign: 'middle', + backgroundClip: 'padding-box', + boxSizing: 'border-box', + WebkitTapHighlightColor: 'transparent', + flexShrink: 0, + userSelect: 'none', + display: 'inline-flex', + alignItems: 'center', + fontFamily: theme.vars.ff.sans, + fontWeight: theme.vars.fw[2], + backgroundColor: 'white', + borderWidth: '1px', + borderStyle: 'solid', + boxShadow: + '0 1px 2px rgba(0, 0, 0, 0.05),\n 0 2px 4px -1px rgba(0, 0, 0, 0.05),\n 0 4px 8px -2px rgba(0, 0, 0, 0.05)', + border: '1px solid hsl(0deg 0% 0% / 5%)', + borderRadius: '6px', + color: theme.vars.gray.text[2], + height: theme.vars.space[6], + paddingLeft: theme.vars.space[2], + paddingRight: theme.vars.space[5], + fontSize: theme.vars.fs[2], + '&:focus-visible': { + outlineWidth: '2px', + outlineStyle: 'solid', + outlineColor: 'black', + outlineOffset: '2px', + }, + '@media screen and (hover: hover)': { + '&:hover': { borderColor: 'hsl(0deg 0% 0% / 13%)' }, + }, +})); + +const Positioner = styled(Menu.Positioner)` + &:focus-visible { + outline: none; + } +`; +const Popup = styled(Menu.Popup)(({ theme }) => ({ + fontFamily: theme.vars.ff.sans, + fontWeight: theme.vars.fw[2], + backgroundColor: 'white', + borderWidth: '1px', + borderStyle: 'solid', + boxShadow: + '0 1px 2px rgba(0, 0, 0, 0.05),\n 0 2px 4px -1px rgba(0, 0, 0, 0.05),\n 0 4px 8px -2px rgba(0, 0, 0, 0.05)', + border: '1px solid hsl(0deg 0% 0% / 5%)', + padding: '4px', + borderRadius: '6px', + color: theme.vars.gray.text[2], + fontSize: theme.vars.fs[2], +})); + +const Adornment = styled.span` + top: 0; + right: 4px; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + pointer-events: none; + position: absolute; +`; + +const MenuItem = styled(Menu.Item)` + display: block; + text-decoration: none; + padding: var(--space-2); + color: inherit; + border-radius: 3px; + + &:focus-visible { + outline: none; + } + + &:focus { + background-color: hsl(0deg 0% 0% / 5%); + } +`; + +export function DocsVersionSelector(props: DocsVersionSelectorProps) { + const { versions, currentVersion } = props; + + return ( + + + {currentVersion} + + + + + + + {versions.map((version) => ( + {version.version}} key={version.version}> + {version.version} + + ))} + + + + ); +} diff --git a/docs/src/components/EditPageOnGithub.tsx b/docs/src/components/EditPageOnGithub.tsx new file mode 100644 index 00000000..d7dcedd5 --- /dev/null +++ b/docs/src/components/EditPageOnGithub.tsx @@ -0,0 +1,38 @@ +import { styled } from '@pigment-css/react'; +import * as React from 'react'; + +export interface EditPageOnGithubProps { + category: string; + slug: string; +} + +const REPO_ROOT = 'https://github.com/mui/pigment-css'; +// #default-branch-switch +const DEFAULT_BRANCH = 'master'; + +const Link = styled.a(({ theme }) => ({ + cursor: 'pointer', + textDecoration: 'underline', + textDecorationLine: 'underline', + textDecorationStyle: 'solid', + textDecorationThickness: '1px', + textUnderlineOffset: 'calc(0.1em + 3px)', + color: 'inherit', + position: 'relative', + zIndex: 0, + fontSize: theme.vars.fs[3], + lineHeight: '20px', + fontFamily: theme.vars.ff.sans, + '@media screen and (hover: hover)': { '&:hover': { textDecoration: 'none' } }, +})); + +export function EditPageOnGithub(props: EditPageOnGithubProps) { + const { category, slug } = props; + + const url = `${REPO_ROOT}/edit/${DEFAULT_BRANCH}/docs/data/${category}/${slug}/${slug}.mdx`; + return ( + + Edit this page on GitHub + + ); +} diff --git a/docs/src/components/IconButton.tsx b/docs/src/components/IconButton.tsx new file mode 100644 index 00000000..3b1b7b73 --- /dev/null +++ b/docs/src/components/IconButton.tsx @@ -0,0 +1,72 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import { css } from '@pigment-css/react'; +import { Tooltip } from './Tooltip'; + +export const classes = { + root: css` + all: unset; + line-height: 1; + vertical-align: middle; + background-clip: padding-box; + box-sizing: border-box; + -webkit-tap-highlight-color: transparent; + flex-shrink: 0; + user-select: none; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: var(--br-circle); + color: var(--gray-text-1); + position: relative; + + @media screen and (hover: hover) { + &:hover { + background-color: var(--gray-container-2); + } + } + + &:focus-visible { + outline: black solid 2px; + outline-offset: 2px; + } + `, + 's-1': css` + width: var(--space-6); + height: var(--space-6); + `, + 's-2': css` + width: var(--space-7); + height: var(--space-7); + `, + 's-3': css` + width: var(--space-8); + height: var(--space-8); + `, +}; + +export function IconButton(props: IconButton.Props) { + const { size = 1, label, withTooltip, ...other } = props; + const button = ( +