diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0cdc216d7a..f893380efb 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -48,8 +48,6 @@ jobs: run: npm run typecheck - name: 🔤 Spell Check run: npm run spellcheck - - name: 🧹 ESLint - run: npm run eslint # Docusaurus derives a heading's anchor from its text, so renaming a # heading moves the anchor and breaks every inbound link. An explicit id # survives the rename, and lets a translation keep the English anchor diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 455af04582..a009111539 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,7 +88,7 @@ This builds the English site only, the same build as a Vercel preview. ### Linting Documentation -This repository uses [Prettier](https://prettier.io/), an opinionated code formatter, in order to keep consistent formatting throughout the documentation. Run the following command to automatically fix all formatting, and then push any changes: +This repository uses [ESLint](https://eslint.org/) to catch markup the build cannot check, such as raw links and headings and misaligned JSDoc comments, and [Prettier](https://prettier.io/), an opinionated code formatter, to keep consistent formatting throughout the documentation. Run the following command to report ESLint errors and automatically fix all formatting, and then push any changes: ``` npm run lint @@ -230,7 +230,7 @@ The build only checks links and anchors that go through Markdown or these compon Markup inside code samples is exempt, since it's example code rather than part of the page. CI fails on a raw `` or heading anywhere else. To check before pushing, run: ``` -npm run eslint +npm run lint ``` ### Playgrounds diff --git a/eslint.config.mjs b/eslint.config.mjs index bb23e11294..6fbf665e7f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,5 +1,6 @@ import docusaurus from '@docusaurus/eslint-plugin'; import tsParser from '@typescript-eslint/parser'; +import jsdoc from 'eslint-plugin-jsdoc'; import * as mdx from 'eslint-plugin-mdx'; import versions from './versions.json' with { type: 'json' }; @@ -38,6 +39,18 @@ export default [ plugins: { '@docusaurus': docusaurus }, rules, }, + { + // Keeps every line of a JSDoc block starting with an aligned ` * `. + files: ['src/**/*.{js,jsx,ts,tsx}'], + // An ejected swizzle that disables a rule from a plugin this repo does not install. + ignores: ['src/theme/prism-include-languages.ts'], + languageOptions: { parser: tsParser }, + plugins: { jsdoc }, + rules: { + 'jsdoc/check-alignment': 'error', + 'jsdoc/require-asterisk-prefix': 'error', + }, + }, { ...mdx.flat, // Code samples are page content, not markup, so they are not linted. diff --git a/package-lock.json b/package-lock.json index 2ff24c72ff..5aa56d5b7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,7 @@ "@typescript-eslint/parser": "^8.72.1", "cspell": "^10.0.1", "eslint": "^10.14.0", + "eslint-plugin-jsdoc": "^65.4.2", "eslint-plugin-mdx": "^3.9.1", "hygen": "^6.2.11", "prettier": "^3.9.6", @@ -5064,6 +5065,104 @@ "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", "peer": true }, + "node_modules/@es-joy/escodegen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@es-joy/escodegen/-/escodegen-4.2.0.tgz", + "integrity": "sha512-jWqqS3StW6vGwJeT8yu6alS4nipOBi3kD/pR63+9hG2NfGafCJ4viDME15mVf+fyupyptcUJDXfv63gFF5Gzfg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@es-joy/estraverse": "^7.1.1", + "@es-joy/jsdoccomment": "^0.62.0", + "esprima": "^4.0.1", + "esutils": "^2.0.3", + "optionator": "^0.9.4" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.7.6" + } + }, + "node_modules/@es-joy/escodegen/node_modules/@es-joy/jsdoccomment": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.62.0.tgz", + "integrity": "sha512-yWi6sm7INEwnfS7IJvE0dU+RTrwzLPFcY7e7eGpu/l5Q9lWfQ2ROwZ0qVnc242jw2TUPsfHX3XMIISkGBv57RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.8", + "@typescript-eslint/types": "^8.44.1", + "comment-parser": "1.4.1", + "esquery": "^1.6.0", + "jsdoc-type-pratt-parser": "~5.9.0" + }, + "engines": { + "node": ">=20.11.0" + } + }, + "node_modules/@es-joy/escodegen/node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@es-joy/escodegen/node_modules/jsdoc-type-pratt-parser": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-5.9.2.tgz", + "integrity": "sha512-TYzkACp/wPvDJLRY7qpHXtrhgwoAaojIOnLaaVNi+AbPU2u1kkjfKd9hXXTq0qSAGsyYXvwUXt99h9I5iCmjjw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@es-joy/estraverse": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@es-joy/estraverse/-/estraverse-7.1.1.tgz", + "integrity": "sha512-h2d737iS0LFAqypZOmXizu54qAbHofI/RpD30Zom03O6BCXKHuFevWKPcGarZbGq82h7qSBb5G6kLdhTMO+caw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-1.0.0.tgz", + "integrity": "sha512-jiVfUnvMioo3upHpAoxE4XkqfrL6nc/XQx7Uw7tQL6QbQDJcALm+fhdj6tjiORDF7uADJbTlmnYWR9DvJE/eiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.9", + "@typescript-eslint/types": "^8.72.0", + "comment-parser": "1.4.9", + "esquery": "^1.7.0", + "jsdoc-type-pratt-parser": "~9.4.0" + }, + "engines": { + "node": "^22.22.2 || >=24.15.0" + } + }, + "node_modules/@es-joy/resolve.exports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@es-joy/resolve.exports/-/resolve.exports-1.2.0.tgz", + "integrity": "sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", @@ -7416,6 +7515,19 @@ "integrity": "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==", "license": "MIT" }, + "node_modules/@sindresorhus/base62": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/base62/-/base62-1.0.0.tgz", + "integrity": "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@slorber/remark-comment": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@slorber/remark-comment/-/remark-comment-1.0.0.tgz", @@ -9300,6 +9412,16 @@ "node": ">= 8" } }, + "node_modules/are-docs-informative": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.1.1.tgz", + "integrity": "sha512-sqRsNQBwbKLRX0jV5Cu5uzmtflf892n4Vukz7T659ebL4pz3mpOqCMU7lxMoBTFwnp10E3YB5ZcyHM41W5bcDA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -10320,6 +10442,16 @@ "node": ">= 6" } }, + "node_modules/comment-parser": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.9.tgz", + "integrity": "sha512-+2AvZKjJaNq9GQljm3tr0utwrig5BL+jgJGvz5rDpGKNIp+ojcRXWUwBbh/sGIi1QCprR6PsKFakub+w1v+4hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/common-path-prefix": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", @@ -12034,6 +12166,67 @@ "node": ">=4.0" } }, + "node_modules/eslint-plugin-jsdoc": { + "version": "65.4.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-65.4.2.tgz", + "integrity": "sha512-+1XHeMXZxHK0+uiHwKuZzwCIlZjw9SuZ/enuLMtZ8ZT7cxBLTZ4lCHd3kO6BhPcsUQ59fMg6DKWcCNxnRAhMjg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/escodegen": "^4.2.0", + "@es-joy/jsdoccomment": "~1.0.0", + "@es-joy/resolve.exports": "1.2.0", + "@typescript-eslint/utils": "^8.72.0", + "are-docs-informative": "^0.1.1", + "comment-parser": "1.4.9", + "debug": "^4.4.3", + "escape-string-regexp": "^5.0.0", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "html-entities": "^2.6.0", + "object-deep-merge": "^2.0.1", + "parse-imports-exports": "^0.2.4", + "semver": "^7.8.5", + "spdx-expression-parse": "^5.0.0", + "to-valid-identifier": "^1.0.0" + }, + "engines": { + "node": "^22.22.2 || >=24.15.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/spdx-expression-parse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-5.0.0.tgz", + "integrity": "sha512-vngmw3Rgn+o2arXNbnZaj5UtOEBuWBfvaI+Wc8GFfykIhA5/vdK9/Sp/XkLv63dykz2rxKDvKEHupF5P0FORcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/eslint-plugin-mdx": { "version": "3.9.4", "resolved": "https://registry.npmjs.org/eslint-plugin-mdx/-/eslint-plugin-mdx-3.9.4.tgz", @@ -13686,6 +13879,23 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -14836,6 +15046,19 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-9.4.0.tgz", + "integrity": "sha512-orXhi2Y06rag47r4SW4JPII6WlUJbp0xe9cIcqLaSbb/8tREcEZSq99Lu3AFfra6PAlMqTYIxV+ZHxl1dFUrWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.9" + }, + "engines": { + "node": "^22.22.2 || >=24.15.0" + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -18052,6 +18275,13 @@ "node": ">=0.10.0" } }, + "node_modules/object-deep-merge": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-2.0.1.tgz", + "integrity": "sha512-aKttDKcU3pyZqKcCkDhsMn70WmZFG2JGDQLP9EcLyTSIFQRCPWLAmBZRLJnrVUrhPG1jETEEbfdgbNtJf1LyMg==", + "dev": true, + "license": "MIT" + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -18534,6 +18764,16 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" }, + "node_modules/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-statements": "1.0.11" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -18557,6 +18797,13 @@ "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==", "license": "ISC" }, + "node_modules/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "dev": true, + "license": "MIT" + }, "node_modules/parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", @@ -21162,6 +21409,19 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "license": "MIT" }, + "node_modules/reserved-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz", + "integrity": "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -22000,11 +22260,12 @@ } }, "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "license": "BSD-3-Clause", "engines": { - "node": ">= 8" + "node": ">= 12" } }, "node_modules/source-map-js": { @@ -22720,6 +22981,23 @@ "node": ">=8.0" } }, + "node_modules/to-valid-identifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-valid-identifier/-/to-valid-identifier-1.0.0.tgz", + "integrity": "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/base62": "^1.0.0", + "reserved-identifiers": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", diff --git a/package.json b/package.json index 717b2bfa1f..63dbb3a471 100644 --- a/package.json +++ b/package.json @@ -14,10 +14,9 @@ "crowdin:sync": "docusaurus write-translations && crowdin upload && crowdin download", "deploy": "docusaurus deploy", "docusaurus": "docusaurus", - "eslint": "eslint src docs versioned_docs --cache", "generate-markdown": "node scripts/native.mjs && node scripts/cli.mjs && node scripts/release-notes.mjs", "heading-ids": "docusaurus write-heading-ids . \"docs/**/*.mdx\" \"!docs/native/**\" \"!docs/cli/commands/**\" --syntax mdx-comment", - "lint": "npm run prettier -- --write", + "lint": "eslint src docs versioned_docs --cache && npm run prettier -- --write", "serve": "docusaurus serve", "playground:new": "hygen playground new", "prestart": "npm run generate-markdown", @@ -75,6 +74,7 @@ "@typescript-eslint/parser": "^8.72.1", "cspell": "^10.0.1", "eslint": "^10.14.0", + "eslint-plugin-jsdoc": "^65.4.2", "eslint-plugin-mdx": "^3.9.1", "hygen": "^6.2.11", "prettier": "^3.9.6", diff --git a/src/components/global/DocsCard/index.tsx b/src/components/global/DocsCard/index.tsx index 722f2a46c0..6007ab53a6 100644 --- a/src/components/global/DocsCard/index.tsx +++ b/src/components/global/DocsCard/index.tsx @@ -16,26 +16,26 @@ interface Props extends React.HTMLAttributes { function DocsCard(props: Props): ReactNode { const isStatic = typeof props.href === 'undefined'; - const header = props.header === 'undefined' ? null :
{props.header}
; + const header = props.header === 'undefined' ? null :
{props.header}
; const hoverIcon = props.hoverIcon || props.icon; const content = ( <> - {props.img && } -
+ {props.img && } +
{(props.icon || hoverIcon) && ( -
- {props.icon && } - {hoverIcon && } +
+ {props.icon && } + {hoverIcon && }
)} {props.iconset && ( -
+
{props.iconset.split(',').map((icon, index, array) => ( - + {index < array.length - 1 && ( -
+
+
)} @@ -44,15 +44,12 @@ function DocsCard(props: Props): ReactNode {
)} {props.header && header} -
{props.children}
+
{props.children}
); - const className = clsx(props.className, { - 'Card-with-image': typeof props.img !== 'undefined', - 'Card-without-image': typeof props.img === 'undefined', - }); + const className = clsx(props.className, typeof props.img !== 'undefined' ? styles.withImage : styles.withoutImage); if (isStatic) { return ( diff --git a/src/components/global/DocsCard/styles.module.scss b/src/components/global/DocsCard/styles.module.scss index b89fbb3012..2998b05beb 100644 --- a/src/components/global/DocsCard/styles.module.scss +++ b/src/components/global/DocsCard/styles.module.scss @@ -43,7 +43,7 @@ docs-card { box-shadow: var(--docs-card-shadow); text-decoration: none; - :global(.Card-icon-hover) { + .iconHover { opacity: 1; } } @@ -55,88 +55,86 @@ docs-card { color: inherit; } -:global { - .Card-with-image { - grid-row: span 2; - } +.withImage { + grid-row: span 2; +} - .Card-without-image { - font-size: 12px; - } +.withoutImage { + font-size: 12px; +} - .Card-container { - padding: 1.5rem; - } +.container { + padding: 1.5rem; +} - .Card-image { - width: 100%; - height: auto; - } +.image { + width: 100%; + height: auto; +} - .Card-icon { - width: 48px; - height: 48px; +.icon { + width: 48px; + height: 48px; - margin-bottom: 1.2em; + margin-bottom: 1.2em; - border-radius: 50%; - object-fit: contain; - } + border-radius: 50%; + object-fit: contain; +} - .Card-icon-default { - transition: opacity 150ms ease-out; - } +.iconDefault { + transition: opacity 150ms ease-out; +} - .Card-icon-hover { - top: 0; - position: absolute; +.iconHover { + top: 0; + position: absolute; - opacity: 0; + opacity: 0; - transition: opacity 150ms ease-out; - } + transition: opacity 150ms ease-out; +} - .Card-icon-row { - position: relative; - display: flex; - gap: 8px; - } +.iconRow { + position: relative; + display: flex; + gap: 8px; +} - .Card-plus-icon { - display: flex; - align-items: center; - justify-content: center; - width: 24px; - height: 48px; - color: var(--docs-card-border-c); - font-size: 22px; - font-weight: 600; - } +.plusIcon { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 48px; + color: var(--docs-card-border-c); + font-size: 22px; + font-weight: 600; +} - .Card-content > *:first-child { - margin-top: 0; - } +.content > *:first-child { + margin-top: 0; +} - .Card-content > *:last-child { - margin-bottom: 0; - } +.content > *:last-child { + margin-bottom: 0; +} - .Card-content > * { - margin: 0 0 0.65em; - } +.content > * { + margin: 0 0 0.65em; +} - .Card-content p { - font-size: 0.75rem; - } +.content p { + font-size: 0.75rem; +} - .Card-header { - font-size: 1rem; - line-height: 1.4; - margin-bottom: 0.45em; - font-weight: 600; - } +.header { + font-size: 1rem; + line-height: 1.4; + margin-bottom: 0.45em; + font-weight: 600; +} - .Card-without-image .Card-header { - margin-bottom: 0.3em; - } +.withoutImage .header { + margin-bottom: 0.3em; }