mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-11 08:07:26 +02:00
feat(eslint-plugin): new prefer-docusaurus-heading rule (#8384)
This commit is contained in:
parent
a53d4cb2b3
commit
90e7e321d1
31 changed files with 254 additions and 52 deletions
10
.eslintrc.js
vendored
10
.eslintrc.js
vendored
|
@ -375,6 +375,7 @@ module.exports = {
|
|||
'@typescript-eslint/no-unused-vars': [ERROR, {ignoreRestSiblings: true}],
|
||||
'@typescript-eslint/prefer-optional-chain': ERROR,
|
||||
'@docusaurus/no-html-links': ERROR,
|
||||
'@docusaurus/prefer-docusaurus-heading': ERROR,
|
||||
'@docusaurus/no-untranslated-text': [
|
||||
WARNING,
|
||||
{
|
||||
|
@ -495,5 +496,14 @@ module.exports = {
|
|||
files: ['packages/eslint-plugin/**/*.{js,ts}'],
|
||||
extends: ['plugin:eslint-plugin/recommended'],
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'packages/docusaurus-plugin-debug/**',
|
||||
'packages/docusaurus/src/**',
|
||||
],
|
||||
rules: {
|
||||
'@docusaurus/prefer-docusaurus-heading': OFF,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import Heading from '@theme/Heading';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
type FeatureItem = {
|
||||
|
@ -48,7 +49,7 @@ function Feature({title, Svg, description}: FeatureItem) {
|
|||
<Svg className={styles.featureSvg} role="img" />
|
||||
</div>
|
||||
<div className="text--center padding-horiz--md">
|
||||
<h3>{title}</h3>
|
||||
<Heading as="h3">{title}</Heading>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,6 +4,7 @@ import Link from '@docusaurus/Link';
|
|||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import Layout from '@theme/Layout';
|
||||
import HomepageFeatures from '@site/src/components/HomepageFeatures';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
import styles from './index.module.css';
|
||||
|
||||
|
@ -12,7 +13,9 @@ function HomepageHeader() {
|
|||
return (
|
||||
<header className={clsx('hero hero--primary', styles.heroBanner)}>
|
||||
<div className="container">
|
||||
<h1 className="hero__title">{siteConfig.title}</h1>
|
||||
<Heading as="h1" className="hero__title">
|
||||
{siteConfig.title}
|
||||
</Heading>
|
||||
<p className="hero__subtitle">{siteConfig.tagline}</p>
|
||||
<div className={styles.buttons}>
|
||||
<Link
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import Heading from '@theme/Heading';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
const FeatureList = [
|
||||
|
@ -42,7 +43,7 @@ function Feature({Svg, title, description}) {
|
|||
<Svg className={styles.featureSvg} role="img" />
|
||||
</div>
|
||||
<div className="text--center padding-horiz--md">
|
||||
<h3>{title}</h3>
|
||||
<Heading as="h3">{title}</Heading>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,7 @@ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|||
import Layout from '@theme/Layout';
|
||||
import HomepageFeatures from '@site/src/components/HomepageFeatures';
|
||||
|
||||
import Heading from '@theme/Heading';
|
||||
import styles from './index.module.css';
|
||||
|
||||
function HomepageHeader() {
|
||||
|
@ -12,7 +13,9 @@ function HomepageHeader() {
|
|||
return (
|
||||
<header className={clsx('hero hero--primary', styles.heroBanner)}>
|
||||
<div className="container">
|
||||
<h1 className="hero__title">{siteConfig.title}</h1>
|
||||
<Heading as="h1" className="hero__title">
|
||||
{siteConfig.title}
|
||||
</Heading>
|
||||
<p className="hero__subtitle">{siteConfig.tagline}</p>
|
||||
<div className={styles.buttons}>
|
||||
<Link
|
||||
|
|
|
@ -11,6 +11,7 @@ import {translate} from '@docusaurus/Translate';
|
|||
import {PageMetadata} from '@docusaurus/theme-common';
|
||||
import Layout from '@theme/Layout';
|
||||
import type {ArchiveBlogPost, Props} from '@theme/BlogArchivePage';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
type YearProp = {
|
||||
year: string;
|
||||
|
@ -20,7 +21,9 @@ type YearProp = {
|
|||
function Year({year, posts}: YearProp) {
|
||||
return (
|
||||
<>
|
||||
<h3>{year}</h3>
|
||||
<Heading as="h3" id={year}>
|
||||
{year}
|
||||
</Heading>
|
||||
<ul>
|
||||
{posts.map((post) => (
|
||||
<li key={post.metadata.date}>
|
||||
|
@ -81,7 +84,9 @@ export default function BlogArchive({archive}: Props): JSX.Element {
|
|||
<Layout>
|
||||
<header className="hero hero--primary">
|
||||
<div className="container">
|
||||
<h1 className="hero__title">{title}</h1>
|
||||
<Heading as="h1" className="hero__title">
|
||||
{title}
|
||||
</Heading>
|
||||
<p className="hero__subtitle">{description}</p>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
@ -17,6 +17,7 @@ import BlogLayout from '@theme/BlogLayout';
|
|||
import TagsListByLetter from '@theme/TagsListByLetter';
|
||||
import type {Props} from '@theme/BlogTagsListPage';
|
||||
import SearchMetadata from '@theme/SearchMetadata';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
export default function BlogTagsListPage({tags, sidebar}: Props): JSX.Element {
|
||||
const title = translateTagsPageTitle();
|
||||
|
@ -29,7 +30,7 @@ export default function BlogTagsListPage({tags, sidebar}: Props): JSX.Element {
|
|||
<PageMetadata title={title} />
|
||||
<SearchMetadata tag="blog_tags_list" />
|
||||
<BlogLayout sidebar={sidebar}>
|
||||
<h1>{title}</h1>
|
||||
<Heading as="h1">{title}</Heading>
|
||||
<TagsListByLetter tags={tags} />
|
||||
</BlogLayout>
|
||||
</HtmlClassNameProvider>
|
||||
|
|
|
@ -21,6 +21,7 @@ import SearchMetadata from '@theme/SearchMetadata';
|
|||
import type {Props} from '@theme/BlogTagsPostsPage';
|
||||
import BlogPostItems from '@theme/BlogPostItems';
|
||||
import Unlisted from '@theme/Unlisted';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
// Very simple pluralization: probably good enough for now
|
||||
function useBlogPostsPlural() {
|
||||
|
@ -73,7 +74,7 @@ function BlogTagsPostsPageContent({
|
|||
<BlogLayout sidebar={sidebar}>
|
||||
{tag.unlisted && <Unlisted />}
|
||||
<header className="margin-bottom--xl">
|
||||
<h1>{title}</h1>
|
||||
<Heading as="h1">{title}</Heading>
|
||||
<Link href={tag.allTagsPath}>
|
||||
<Translate
|
||||
id="theme.tags.tagsPageLink"
|
||||
|
|
|
@ -16,6 +16,7 @@ import isInternalUrl from '@docusaurus/isInternalUrl';
|
|||
import {translate} from '@docusaurus/Translate';
|
||||
|
||||
import type {Props} from '@theme/DocCard';
|
||||
import Heading from '@theme/Heading';
|
||||
import type {
|
||||
PropSidebarItemCategory,
|
||||
PropSidebarItemLink,
|
||||
|
@ -52,9 +53,12 @@ function CardLayout({
|
|||
}): JSX.Element {
|
||||
return (
|
||||
<CardContainer href={href}>
|
||||
<h2 className={clsx('text--truncate', styles.cardTitle)} title={title}>
|
||||
<Heading
|
||||
as="h2"
|
||||
className={clsx('text--truncate', styles.cardTitle)}
|
||||
title={title}>
|
||||
{icon} {title}
|
||||
</h2>
|
||||
</Heading>
|
||||
{description && (
|
||||
<p
|
||||
className={clsx('text--truncate', styles.cardDescription)}
|
||||
|
|
|
@ -18,6 +18,7 @@ import Translate, {translate} from '@docusaurus/Translate';
|
|||
import SearchMetadata from '@theme/SearchMetadata';
|
||||
import type {Props} from '@theme/DocTagDocListPage';
|
||||
import Unlisted from '@theme/Unlisted';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
// Very simple pluralization: probably good enough for now
|
||||
function useNDocsTaggedPlural() {
|
||||
|
@ -53,7 +54,7 @@ function DocItem({doc}: {doc: Props['tag']['items'][number]}): JSX.Element {
|
|||
return (
|
||||
<article className="margin-vert--lg">
|
||||
<Link to={doc.permalink}>
|
||||
<h2>{doc.title}</h2>
|
||||
<Heading as="h2">{doc.title}</Heading>
|
||||
</Link>
|
||||
{doc.description && <p>{doc.description}</p>}
|
||||
</article>
|
||||
|
@ -83,7 +84,7 @@ function DocTagDocListPageContent({
|
|||
<main className="col col--8 col--offset-2">
|
||||
{tag.unlisted && <Unlisted />}
|
||||
<header className="margin-bottom--xl">
|
||||
<h1>{title}</h1>
|
||||
<Heading as="h1">{title}</Heading>
|
||||
<Link href={tag.allTagsPath}>
|
||||
<Translate
|
||||
id="theme.tags.tagsPageLink"
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
import TagsListByLetter from '@theme/TagsListByLetter';
|
||||
import SearchMetadata from '@theme/SearchMetadata';
|
||||
import type {Props} from '@theme/DocTagsListPage';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
function DocTagsListPageMetadata({
|
||||
title,
|
||||
|
@ -38,7 +39,7 @@ function DocTagsListPageContent({
|
|||
<div className="container margin-vert--lg">
|
||||
<div className="row">
|
||||
<main className="col col--8 col--offset-2">
|
||||
<h1>{title}</h1>
|
||||
<Heading as="h1">{title}</Heading>
|
||||
<TagsListByLetter tags={tags} />
|
||||
</main>
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,7 @@ import React from 'react';
|
|||
import Translate from '@docusaurus/Translate';
|
||||
import {ErrorBoundaryTryAgainButton} from '@docusaurus/theme-common';
|
||||
import type {Props} from '@theme/Error';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
export default function ErrorPageContent({
|
||||
error,
|
||||
|
@ -18,13 +19,13 @@ export default function ErrorPageContent({
|
|||
<main className="container margin-vert--xl">
|
||||
<div className="row">
|
||||
<div className="col col--6 col--offset-3">
|
||||
<h1 className="hero__title">
|
||||
<Heading as="h1" className="hero__title">
|
||||
<Translate
|
||||
id="theme.ErrorPageContent.title"
|
||||
description="The title of the fallback page when the page crashed">
|
||||
This page crashed.
|
||||
</Translate>
|
||||
</h1>
|
||||
</Heading>
|
||||
<p>{error.message}</p>
|
||||
<div>
|
||||
<ErrorBoundaryTryAgainButton onClick={tryAgain} />
|
||||
|
|
|
@ -9,19 +9,20 @@ import React from 'react';
|
|||
import clsx from 'clsx';
|
||||
import Translate from '@docusaurus/Translate';
|
||||
import type {Props} from '@theme/NotFound/Content';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
export default function NotFoundContent({className}: Props): JSX.Element {
|
||||
return (
|
||||
<main className={clsx('container margin-vert--xl', className)}>
|
||||
<div className="row">
|
||||
<div className="col col--6 col--offset-3">
|
||||
<h1 className="hero__title">
|
||||
<Heading as="h1" className="hero__title">
|
||||
<Translate
|
||||
id="theme.NotFound.title"
|
||||
description="The title of the 404 page">
|
||||
Page Not Found
|
||||
</Translate>
|
||||
</h1>
|
||||
</Heading>
|
||||
<p>
|
||||
<Translate
|
||||
id="theme.NotFound.p1"
|
||||
|
|
|
@ -9,13 +9,15 @@ import React from 'react';
|
|||
import {listTagsByLetters, type TagLetterEntry} from '@docusaurus/theme-common';
|
||||
import Tag from '@theme/Tag';
|
||||
import type {Props} from '@theme/TagsListByLetter';
|
||||
|
||||
import Heading from '@theme/Heading';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
function TagLetterEntryItem({letterEntry}: {letterEntry: TagLetterEntry}) {
|
||||
return (
|
||||
<article>
|
||||
<h2>{letterEntry.letter}</h2>
|
||||
<Heading as="h2" id={letterEntry.letter}>
|
||||
{letterEntry.letter}
|
||||
</Heading>
|
||||
<ul className="padding--none">
|
||||
{letterEntry.tags.map((tag) => (
|
||||
<li key={tag.permalink} className={styles.tag}>
|
||||
|
|
|
@ -33,7 +33,7 @@ import {
|
|||
useSearchResultUrlProcessor,
|
||||
} from '@docusaurus/theme-search-algolia/client';
|
||||
import Layout from '@theme/Layout';
|
||||
|
||||
import Heading from '@theme/Heading';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
// Very simple pluralization: probably good enough for now
|
||||
|
@ -374,7 +374,7 @@ function SearchPageContent(): JSX.Element {
|
|||
</Head>
|
||||
|
||||
<div className="container margin-vert--lg">
|
||||
<h1>{getTitle()}</h1>
|
||||
<Heading as="h1">{getTitle()}</Heading>
|
||||
|
||||
<form className="row" onSubmit={(e) => e.preventDefault()}>
|
||||
<div
|
||||
|
@ -455,9 +455,9 @@ function SearchPageContent(): JSX.Element {
|
|||
{searchResultState.items.map(
|
||||
({title, url, summary, breadcrumbs}, i) => (
|
||||
<article key={i} className={styles.searchResultItem}>
|
||||
<h2 className={styles.searchResultItemHeading}>
|
||||
<Heading as="h2" className={styles.searchResultItemHeading}>
|
||||
<Link to={url} dangerouslySetInnerHTML={{__html: title}} />
|
||||
</h2>
|
||||
</Heading>
|
||||
|
||||
{breadcrumbs.length > 0 && (
|
||||
<nav aria-label="breadcrumbs">
|
||||
|
|
|
@ -15,6 +15,7 @@ export = {
|
|||
rules: {
|
||||
'@docusaurus/string-literal-i18n-messages': 'error',
|
||||
'@docusaurus/no-html-links': 'warn',
|
||||
'@docusaurus/prefer-docusaurus-heading': 'warn',
|
||||
},
|
||||
},
|
||||
all: {
|
||||
|
@ -23,6 +24,7 @@ export = {
|
|||
'@docusaurus/string-literal-i18n-messages': 'error',
|
||||
'@docusaurus/no-untranslated-text': 'warn',
|
||||
'@docusaurus/no-html-links': 'warn',
|
||||
'@docusaurus/prefer-docusaurus-heading': 'warn',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import rule from '../prefer-docusaurus-heading';
|
||||
import {RuleTester} from './testUtils';
|
||||
|
||||
const errorsJSX = [{messageId: 'headings'}] as const;
|
||||
|
||||
const ruleTester = new RuleTester({
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
ruleTester.run('prefer-docusaurus-heading', rule, {
|
||||
valid: [
|
||||
{
|
||||
code: "<Heading as='h1'>heading 1</Heading>",
|
||||
},
|
||||
{
|
||||
code: "<Heading as='h2'>heading 2</Heading>",
|
||||
},
|
||||
{
|
||||
code: "<Heading as='h3'>heading 3</Heading>",
|
||||
},
|
||||
{
|
||||
code: "<Heading as='h4'>heading 4</Heading>",
|
||||
},
|
||||
{
|
||||
code: "<Heading as='h5'>heading 5</Heading>",
|
||||
},
|
||||
{
|
||||
code: "<Heading as='h6'>heading 6</Heading>",
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: '<h1>heading 1</h1>',
|
||||
errors: errorsJSX,
|
||||
},
|
||||
{
|
||||
code: '<h2>heading 2</h2>',
|
||||
errors: errorsJSX,
|
||||
},
|
||||
{
|
||||
code: '<h3>heading 3</h3>',
|
||||
errors: errorsJSX,
|
||||
},
|
||||
{
|
||||
code: '<h4>heading 4</h4>',
|
||||
errors: errorsJSX,
|
||||
},
|
||||
{
|
||||
code: '<h5>heading 5</h5>',
|
||||
errors: errorsJSX,
|
||||
},
|
||||
{
|
||||
code: '<h6>heading 6</h6>',
|
||||
errors: errorsJSX,
|
||||
},
|
||||
],
|
||||
});
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import noHtmlLinks from './no-html-links';
|
||||
import preferDocusaurusHeading from './prefer-docusaurus-heading';
|
||||
import noUntranslatedText from './no-untranslated-text';
|
||||
import stringLiteralI18nMessages from './string-literal-i18n-messages';
|
||||
|
||||
|
@ -13,4 +14,5 @@ export default {
|
|||
'no-untranslated-text': noUntranslatedText,
|
||||
'string-literal-i18n-messages': stringLiteralI18nMessages,
|
||||
'no-html-links': noHtmlLinks,
|
||||
'prefer-docusaurus-heading': preferDocusaurusHeading,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {createRule} from '../util';
|
||||
import type {TSESTree} from '@typescript-eslint/types/dist/ts-estree';
|
||||
|
||||
type Options = [];
|
||||
type MessageIds = 'headings';
|
||||
|
||||
export default createRule<Options, MessageIds>({
|
||||
name: 'prefer-docusaurus-heading',
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description:
|
||||
'enforce using Docusaurus theme Heading component instead of any <hn> tag',
|
||||
recommended: false,
|
||||
},
|
||||
schema: [],
|
||||
messages: {
|
||||
headings:
|
||||
'Do not use any of the `<hn>` tags for headings. Use the `<Heading />` component from `@theme/Heading` instead.',
|
||||
},
|
||||
},
|
||||
defaultOptions: [],
|
||||
|
||||
create(context) {
|
||||
const headingTypes = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
|
||||
|
||||
return {
|
||||
JSXOpeningElement(node) {
|
||||
const elementName = (node.name as TSESTree.JSXIdentifier).name;
|
||||
|
||||
if (!headingTypes.includes(elementName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.report({node, messageId: 'headings'});
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
|
@ -7,12 +7,13 @@
|
|||
|
||||
import React from 'react';
|
||||
import Layout from '@theme/Layout';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
// See https://github.com/facebook/docusaurus/issues/6337#issuecomment-1012913647
|
||||
export default function Analytics(): JSX.Element {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Test Analytics</h1>
|
||||
<Heading as="h1">Test Analytics</Heading>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import React from 'react';
|
||||
import Interpolate from '@docusaurus/Interpolate';
|
||||
import Layout from '@theme/Layout';
|
||||
|
||||
import Heading from '@theme/Heading';
|
||||
import ErrorBoundaryTestButton from '@site/src/components/ErrorBoundaryTestButton';
|
||||
|
||||
export default function ErrorBoundaryTests(): JSX.Element {
|
||||
|
@ -17,7 +17,7 @@ export default function ErrorBoundaryTests(): JSX.Element {
|
|||
<ErrorBoundaryTestButton>Crash outside layout</ErrorBoundaryTestButton>
|
||||
<Layout>
|
||||
<main className="container margin-vert--xl">
|
||||
<h1>Error boundary tests</h1>
|
||||
<Heading as="h1">Error boundary tests</Heading>
|
||||
<div>
|
||||
<ErrorBoundaryTestButton>
|
||||
Crash inside layout
|
||||
|
|
|
@ -53,6 +53,7 @@ For more fine-grained control, you can also enable the plugin manually and confi
|
|||
| [`@docusaurus/no-untranslated-text`](./no-untranslated-text.mdx) | Enforce text labels in JSX to be wrapped by translate calls | |
|
||||
| [`@docusaurus/string-literal-i18n-messages`](./string-literal-i18n-messages.mdx) | Enforce translate APIs to be called on plain text labels | ✅ |
|
||||
| [`@docusaurus/no-html-links`](./no-html-links.mdx) | Ensures @docusaurus/Link is used instead of `<a>` tags | ✅ |
|
||||
| [`@docusaurus/prefer-docusaurus-heading`](./prefer-docusaurus-heading.mdx) | Ensures @theme/Heading is used instead of `<hn>` tags for headings | ✅ |
|
||||
|
||||
✅ = recommended
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
slug: /api/misc/@docusaurus/eslint-plugin/prefer-docusaurus-heading
|
||||
---
|
||||
|
||||
# prefer-docusaurus-heading
|
||||
|
||||
Ensures that the `@theme/Heading` theme component provided by Docusaurus [`theme-classic`](../../themes/theme-classic.mdx) is used instead of `<hn>` tags for headings.
|
||||
|
||||
## Rule Details {#details}
|
||||
|
||||
Examples of **incorrect** code for this rule:
|
||||
|
||||
```html
|
||||
<h1>This is heading 1</h1>
|
||||
|
||||
<h2>This is heading 2</h2>
|
||||
|
||||
<h3>This is heading 3</h3>
|
||||
```
|
||||
|
||||
Examples of **correct** code for this rule:
|
||||
|
||||
```javascript
|
||||
import Heading from '@theme/Heading'
|
||||
|
||||
<Heading as='h1'>This is heading 1</Heading>
|
||||
|
||||
<Heading as='h2'>This is heading 2</Heading>
|
||||
|
||||
<Heading as='h3'>This is heading 3</Heading>
|
||||
```
|
|
@ -12,6 +12,7 @@ import clsx from 'clsx';
|
|||
import Translate from '@docusaurus/Translate';
|
||||
import Link from '@docusaurus/Link';
|
||||
import Image from '@theme/IdealImage';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
const Playgrounds = [
|
||||
{
|
||||
|
@ -64,7 +65,7 @@ function PlaygroundCard({name, image, url, description}: Props) {
|
|||
</Link>
|
||||
</div>
|
||||
<div className="card__body">
|
||||
<h3>{name}</h3>
|
||||
<Heading as="h3">{name}</Heading>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
<div className="card__footer">
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import React, {type ReactNode} from 'react';
|
||||
import Translate from '@docusaurus/Translate';
|
||||
import Link from '@docusaurus/Link';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
function WebsiteLink({to, children}: {to: string; children?: ReactNode}) {
|
||||
return (
|
||||
|
@ -45,7 +46,9 @@ function TeamProfileCard({
|
|||
alt={`${name}'s avatar`}
|
||||
/>
|
||||
<div className="avatar__intro">
|
||||
<h3 className="avatar__name">{name}</h3>
|
||||
<Heading as="h3" className="avatar__name">
|
||||
{name}
|
||||
</Heading>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -25,12 +25,13 @@ import ProductHuntCard from '@site/src/components/ProductHuntCard';
|
|||
import HackerNewsIcon from '@site/src/components/HackerNewsIcon';
|
||||
import styles from './styles.module.css';
|
||||
import 'react-lite-youtube-embed/dist/LiteYouTubeEmbed.css';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
function HeroBanner() {
|
||||
return (
|
||||
<div className={styles.hero} data-theme="dark">
|
||||
<div className={styles.heroInner}>
|
||||
<h1 className={styles.heroProjectTagline}>
|
||||
<Heading as="h1" className={styles.heroProjectTagline}>
|
||||
<img
|
||||
alt={translate({message: 'Docusaurus with Keytar'})}
|
||||
className={styles.heroLogo}
|
||||
|
@ -51,7 +52,7 @@ function HeroBanner() {
|
|||
}),
|
||||
}}
|
||||
/>
|
||||
</h1>
|
||||
</Heading>
|
||||
<div className={styles.indexCtas}>
|
||||
<Link className="button button--primary" to="/docs">
|
||||
<Translate>Get Started</Translate>
|
||||
|
@ -109,9 +110,9 @@ function TweetsSection() {
|
|||
return (
|
||||
<div className={clsx(styles.section, styles.sectionAlt)}>
|
||||
<div className="container">
|
||||
<h2 className={clsx('margin-bottom--lg', 'text--center')}>
|
||||
<Heading as="h2" className={clsx('margin-bottom--lg', 'text--center')}>
|
||||
<Translate>Loved by many engineers</Translate>
|
||||
</h2>
|
||||
</Heading>
|
||||
<div className={clsx('row', styles.tweetsSection)}>
|
||||
{tweetColumns.map((tweetItems, i) => (
|
||||
<div className="col col--4" key={i}>
|
||||
|
@ -161,9 +162,9 @@ function VideoContainer() {
|
|||
<div className="container text--center margin-bottom--xl">
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<h2>
|
||||
<Heading as="h2">
|
||||
<Translate>Check it out in the intro video</Translate>
|
||||
</h2>
|
||||
</Heading>
|
||||
<div className="video-container">
|
||||
<LiteYouTubeEmbed
|
||||
id="_An9EsKPhp0"
|
||||
|
@ -198,7 +199,9 @@ function Feature({
|
|||
src={withBaseUrl(feature.image.src)}
|
||||
loading="lazy"
|
||||
/>
|
||||
<h3 className={clsx(styles.featureHeading)}>{feature.title}</h3>
|
||||
<Heading as="h3" className={clsx(styles.featureHeading)}>
|
||||
{feature.title}
|
||||
</Heading>
|
||||
<p className="padding-horiz--md">{feature.text}</p>
|
||||
</div>
|
||||
);
|
||||
|
@ -210,9 +213,9 @@ function FeaturesContainer() {
|
|||
|
||||
return (
|
||||
<div className="container text--center">
|
||||
<h2>
|
||||
<Heading as="h2">
|
||||
<Translate>Main features</Translate>
|
||||
</h2>
|
||||
</Heading>
|
||||
<div className="row margin-bottom--lg">
|
||||
{firstRow.map((feature, idx) => (
|
||||
<Feature feature={feature} key={idx} />
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
type Tag,
|
||||
} from '@site/src/data/users';
|
||||
import {sortBy} from '@site/src/utils/jsUtils';
|
||||
import Heading from '@theme/Heading';
|
||||
import Tooltip from '../ShowcaseTooltip';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
|
@ -76,11 +77,11 @@ function ShowcaseCard({user}: {user: User}) {
|
|||
</div>
|
||||
<div className="card__body">
|
||||
<div className={clsx(styles.showcaseCardHeader)}>
|
||||
<h4 className={styles.showcaseCardTitle}>
|
||||
<Heading as="h4" className={styles.showcaseCardTitle}>
|
||||
<Link href={user.website} className={styles.showcaseCardLink}>
|
||||
{user.title}
|
||||
</Link>
|
||||
</h4>
|
||||
</Heading>
|
||||
{user.tags.includes('favorite') && (
|
||||
<FavoriteIcon svgClass={styles.svgIconFavorite} size="small" />
|
||||
)}
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
type User,
|
||||
type TagType,
|
||||
} from '@site/src/data/users';
|
||||
import Heading from '@theme/Heading';
|
||||
import ShowcaseTagSelect, {
|
||||
readSearchTags,
|
||||
} from './_components/ShowcaseTagSelect';
|
||||
|
@ -122,7 +123,7 @@ function useFilteredUsers() {
|
|||
function ShowcaseHeader() {
|
||||
return (
|
||||
<section className="margin-top--lg margin-bottom--lg text--center">
|
||||
<h1>{TITLE}</h1>
|
||||
<Heading as="h1">{TITLE}</Heading>
|
||||
<p>{DESCRIPTION}</p>
|
||||
<Link className="button button--primary" to={SUBMIT_URL}>
|
||||
<Translate id="showcase.header.button">
|
||||
|
@ -157,9 +158,9 @@ function ShowcaseFilters() {
|
|||
<section className="container margin-top--l margin-bottom--lg">
|
||||
<div className={clsx('margin-bottom--sm', styles.filterCheckbox)}>
|
||||
<div>
|
||||
<h2>
|
||||
<Heading as="h2">
|
||||
<Translate id="showcase.filters.title">Filters</Translate>
|
||||
</h2>
|
||||
</Heading>
|
||||
<span>{siteCountPlural(filteredUsers.length)}</span>
|
||||
</div>
|
||||
<ShowcaseFilterToggle />
|
||||
|
@ -255,9 +256,9 @@ function ShowcaseCards() {
|
|||
return (
|
||||
<section className="margin-top--lg margin-bottom--xl">
|
||||
<div className="container padding-vert--md text--center">
|
||||
<h2>
|
||||
<Heading as="h2">
|
||||
<Translate id="showcase.usersList.noResult">No result</Translate>
|
||||
</h2>
|
||||
</Heading>
|
||||
<SearchBar />
|
||||
</div>
|
||||
</section>
|
||||
|
@ -275,11 +276,11 @@ function ShowcaseCards() {
|
|||
'margin-bottom--md',
|
||||
styles.showcaseFavoriteHeader,
|
||||
)}>
|
||||
<h2>
|
||||
<Heading as="h2">
|
||||
<Translate id="showcase.favoritesList.title">
|
||||
Our favorites
|
||||
</Translate>
|
||||
</h2>
|
||||
</Heading>
|
||||
<FavoriteIcon svgClass={styles.svgIconFavorite} />
|
||||
<SearchBar />
|
||||
</div>
|
||||
|
@ -296,9 +297,9 @@ function ShowcaseCards() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="container margin-top--lg">
|
||||
<h2 className={styles.showcaseHeader}>
|
||||
<Heading as="h2" className={styles.showcaseHeader}>
|
||||
<Translate id="showcase.usersList.allUsers">All sites</Translate>
|
||||
</h2>
|
||||
</Heading>
|
||||
<ul className={clsx('clean-list', styles.showcaseList)}>
|
||||
{otherUsers.map((user) => (
|
||||
<ShowcaseCard key={user.title} user={user} />
|
||||
|
|
|
@ -168,11 +168,11 @@ export default function Version(): JSX.Element {
|
|||
</div>
|
||||
)}
|
||||
<div className="margin-bottom--lg">
|
||||
<h3 id="legacy">
|
||||
<Heading as="h3" id="legacy">
|
||||
<Translate id="versionsPage.legacy.title">
|
||||
Docusaurus v1 (Legacy)
|
||||
</Translate>
|
||||
</h3>
|
||||
</Heading>
|
||||
<p>
|
||||
<Translate id="versionsPage.legacy.description">
|
||||
Here you can find documentation for legacy version of Docusaurus.
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import React from 'react';
|
||||
import Translate from '@docusaurus/Translate';
|
||||
import Link from '@docusaurus/Link';
|
||||
import Heading from '@theme/Heading';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
function TwitterLink() {
|
||||
|
@ -63,7 +64,9 @@ export default function ChangelogListHeader({
|
|||
}): JSX.Element {
|
||||
return (
|
||||
<header className="margin-bottom--lg">
|
||||
<h1 style={{fontSize: '3rem'}}>{blogTitle}</h1>
|
||||
<Heading as="h1" style={{fontSize: '3rem'}}>
|
||||
{blogTitle}
|
||||
</Heading>
|
||||
<p>
|
||||
<Translate
|
||||
id="changelog.description"
|
||||
|
|
|
@ -8,11 +8,14 @@
|
|||
import React from 'react';
|
||||
import type {Props} from '@theme/Admonition';
|
||||
import DefaultAdmonitionTypes from '@theme-original/Admonition/Types';
|
||||
import Heading from '@theme/Heading';
|
||||
|
||||
function MyCustomAdmonition(props: Props): JSX.Element {
|
||||
return (
|
||||
<div style={{border: 'solid red', padding: 10}}>
|
||||
<h5 style={{color: 'blue', fontSize: 30}}>{props.title}</h5>
|
||||
<Heading as="h5" style={{color: 'blue', fontSize: 30}}>
|
||||
{props.title}
|
||||
</Heading>
|
||||
<div>{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue