From c77c58e18143233be042c4980a6ed08ae9beac52 Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Mon, 16 May 2022 19:40:23 +0200 Subject: chore: adjust and complete missing styles * add logo to topics pages and links * add Prism styles to articles * and a few other adjustements --- src/styles/pages/article.module.scss | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/styles/pages/article.module.scss (limited to 'src/styles/pages/article.module.scss') diff --git a/src/styles/pages/article.module.scss b/src/styles/pages/article.module.scss new file mode 100644 index 0000000..a42c633 --- /dev/null +++ b/src/styles/pages/article.module.scss @@ -0,0 +1,36 @@ +@use "@styles/abstracts/functions" as fun; +@use "@styles/abstracts/mixins" as mix; +@use "@styles/abstracts/variables" as var; +@use "partials/article-links"; +@use "partials/article-lists"; +@use "partials/article-media"; +@use "partials/article-prism"; +@use "partials/article-wp-blocks"; + +.btn { + margin-right: var(--spacing-2xs); + padding: var(--spacing-2xs) var(--spacing-xs); + + &__icon { + max-width: fun.convert-px(22); + margin-right: var(--spacing-2xs); + } +} + +.body { + :global { + @include article-links.styles; + @include article-lists.styles; + @include article-media.styles; + @include article-prism.styles; + @include article-wp-blocks.styles; + } +} + +.widget { + @include mix.media("screen") { + @include mix.dimensions("md") { + width: min-content; + } + } +} -- cgit v1.2.3 From 7d9f874364ec6e255e62eb3027011bfed267904c Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Tue, 17 May 2022 18:37:38 +0200 Subject: chore: adjust articles styles * change animation on article card hover * change comments section alignment --- src/components/atoms/buttons/buttons.module.scss | 2 +- src/components/atoms/headings/heading.module.scss | 16 +++- src/components/atoms/headings/heading.stories.tsx | 26 +++++++ src/components/atoms/headings/heading.tsx | 58 ++++++++++++--- .../molecules/buttons/heading-button.module.scss | 15 ++-- .../molecules/buttons/heading-button.stories.tsx | 73 ++++++------------- src/components/organisms/forms/comment-form.tsx | 14 +++- .../organisms/layout/summary.module.scss | 7 +- .../templates/page/page-layout.module.scss | 9 ++- src/components/templates/page/page-layout.tsx | 85 ++++++++++++++-------- src/pages/article/[slug].tsx | 18 ++++- src/styles/pages/article.module.scss | 1 + 12 files changed, 211 insertions(+), 113 deletions(-) (limited to 'src/styles/pages/article.module.scss') diff --git a/src/components/atoms/buttons/buttons.module.scss b/src/components/atoms/buttons/buttons.module.scss index 36c66b6..2444bb1 100644 --- a/src/components/atoms/buttons/buttons.module.scss +++ b/src/components/atoms/buttons/buttons.module.scss @@ -16,7 +16,7 @@ } &--rectangle { - padding: var(--spacing-2xs) var(--spacing-md); + padding: var(--spacing-2xs) var(--spacing-sm); } &--square, diff --git a/src/components/atoms/headings/heading.module.scss b/src/components/atoms/headings/heading.module.scss index 8620f6f..a420bc1 100644 --- a/src/components/atoms/headings/heading.module.scss +++ b/src/components/atoms/headings/heading.module.scss @@ -6,11 +6,23 @@ letter-spacing: 0.01ex; &--regular { - margin: 0; + margin-bottom: 0; + margin-top: 0; + } + + &--left { + text-align: left; + } + + &--center { + width: fit-content; + margin-left: auto; + margin-right: auto; } &--margin { - margin: 0 0 var(--spacing-sm); + margin-top: 0; + margin-bottom: var(--spacing-sm); & + & { margin-top: var(--spacing-md); diff --git a/src/components/atoms/headings/heading.stories.tsx b/src/components/atoms/headings/heading.stories.tsx index da5a718..0e3885d 100644 --- a/src/components/atoms/headings/heading.stories.tsx +++ b/src/components/atoms/headings/heading.stories.tsx @@ -8,10 +8,26 @@ export default { title: 'Atoms/Typography/Headings', component: Heading, args: { + alignment: 'left', isFake: false, withMargin: true, }, argTypes: { + alignment: { + control: { + type: 'select', + }, + description: 'The title alignment.', + options: ['center', 'left'], + table: { + category: 'Options', + defaultValue: { summary: 'left' }, + }, + type: { + name: 'string', + required: false, + }, + }, className: { control: { type: 'text', @@ -32,6 +48,16 @@ export default { required: true, }, }, + id: { + control: { + type: 'text', + }, + description: 'An unique id.', + type: { + name: 'string', + required: false, + }, + }, isFake: { control: { type: 'boolean', diff --git a/src/components/atoms/headings/heading.tsx b/src/components/atoms/headings/heading.tsx index c5bf4ca..e385249 100644 --- a/src/components/atoms/headings/heading.tsx +++ b/src/components/atoms/headings/heading.tsx @@ -1,9 +1,19 @@ -import { FC, ReactNode } from 'react'; +import { + createElement, + ForwardedRef, + forwardRef, + ForwardRefRenderFunction, + ReactNode, +} from 'react'; import styles from './heading.module.scss'; export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6; export type HeadingProps = { + /** + * The title alignment. Default: left; + */ + alignment?: 'center' | 'left'; /** * The heading body. */ @@ -30,31 +40,55 @@ export type HeadingProps = { withMargin?: boolean; }; +type TitleTagProps = Pick & { + tagName: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p'; +}; + +const TitleTag = forwardRef< + HTMLHeadingElement | HTMLParagraphElement, + TitleTagProps +>( + ( + { children, tagName, ...props }, + ref: ForwardedRef + ) => { + return createElement(tagName, { ...props, ref }, children); + } +); +TitleTag.displayName = 'TitleTag'; + /** * Heading component. * * Render an HTML heading element or a paragraph with heading styles. */ -const Heading: FC = ({ - children, - className, - id, - isFake = false, - level, - withMargin = true, -}) => { - const TitleTag = isFake ? `p` : (`h${level}` as keyof JSX.IntrinsicElements); +const Heading: ForwardRefRenderFunction = ( + { + alignment = 'left', + children, + className, + id, + isFake = false, + level, + withMargin = true, + }, + ref: ForwardedRef +) => { + const tagName = isFake ? 'p' : (`h${level}` as TitleTagProps['tagName']); const levelClass = `heading--${level}`; + const alignmentClass = `heading--${alignment}`; const marginClass = withMargin ? 'heading--margin' : 'heading--regular'; return ( {children} ); }; -export default Heading; +export default forwardRef(Heading); diff --git a/src/components/molecules/buttons/heading-button.module.scss b/src/components/molecules/buttons/heading-button.module.scss index 9c278e4..3c69221 100644 --- a/src/components/molecules/buttons/heading-button.module.scss +++ b/src/components/molecules/buttons/heading-button.module.scss @@ -20,6 +20,14 @@ border-bottom: fun.convert-px(2) solid var(--color-primary-dark); cursor: pointer; + .heading { + padding: var(--spacing-2xs) 0; + background: none; + font-size: var(--font-size-xl); + font-weight: 500; + text-align: left; + } + &:hover, &:focus { .icon { @@ -34,10 +42,3 @@ } } } - -.heading { - padding: var(--spacing-2xs) 0; - background: none; - font-size: var(--font-size-xl); - text-align: left; -} diff --git a/src/components/molecules/buttons/heading-button.stories.tsx b/src/components/molecules/buttons/heading-button.stories.tsx index b0e1465..d1b5ed4 100644 --- a/src/components/molecules/buttons/heading-button.stories.tsx +++ b/src/components/molecules/buttons/heading-button.stories.tsx @@ -1,6 +1,5 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'; import { useState } from 'react'; -import { IntlProvider } from 'react-intl'; import HeadingButtonComponent from './heading-button'; /** @@ -10,6 +9,19 @@ export default { title: 'Molecules/Buttons/HeadingButton', component: HeadingButtonComponent, argTypes: { + className: { + control: { + type: 'text', + }, + description: 'Set additional classnames to the button.', + table: { + category: 'Styles', + }, + type: { + name: 'string', + required: false, + }, + }, expanded: { control: { type: null, @@ -53,13 +65,6 @@ export default { }, }, }, - decorators: [ - (Story) => ( - - - - ), - ], } as ComponentMeta; const Template: ComponentStory = ({ @@ -79,55 +84,21 @@ const Template: ComponentStory = ({ }; /** - * Heading Button Stories - Level 1 + * Heading Button Stories - Expanded */ -export const Level1 = Template.bind({}); -Level1.args = { - level: 1, - title: 'Your title', -}; - -/** - * Heading Button Stories - Level 2 - */ -export const Level2 = Template.bind({}); -Level2.args = { +export const Expanded = Template.bind({}); +Expanded.args = { + expanded: true, level: 2, title: 'Your title', }; /** - * Heading Button Stories - Level 3 + * Heading Button Stories - Collapsed */ -export const Level3 = Template.bind({}); -Level3.args = { - level: 3, - title: 'Your title', -}; - -/** - * Heading Button Stories - Level 4 - */ -export const Level4 = Template.bind({}); -Level4.args = { - level: 4, - title: 'Your title', -}; - -/** - * Heading Button Stories - Level 5 - */ -export const Level5 = Template.bind({}); -Level5.args = { - level: 5, - title: 'Your title', -}; - -/** - * Heading Button Stories - Level 6 - */ -export const Level6 = Template.bind({}); -Level6.args = { - level: 6, +export const Collapsed = Template.bind({}); +Collapsed.args = { + expanded: false, + level: 2, title: 'Your title', }; diff --git a/src/components/organisms/forms/comment-form.tsx b/src/components/organisms/forms/comment-form.tsx index 5ff4ea4..b2c725f 100644 --- a/src/components/organisms/forms/comment-form.tsx +++ b/src/components/organisms/forms/comment-form.tsx @@ -1,6 +1,9 @@ import Button from '@components/atoms/buttons/button'; import Form, { type FormProps } from '@components/atoms/forms/form'; -import Heading, { type HeadingLevel } from '@components/atoms/headings/heading'; +import Heading, { + type HeadingProps, + type HeadingLevel, +} from '@components/atoms/headings/heading'; import Spinner from '@components/atoms/loaders/spinner'; import LabelledField from '@components/molecules/forms/labelled-field'; import { FC, ReactNode, useState } from 'react'; @@ -34,7 +37,11 @@ export type CommentFormProps = Pick & { */ title?: string; /** - * The title level. + * The form title alignment. Default: left. + */ + titleAlignment?: HeadingProps['alignment']; + /** + * The title level. Default: 2. */ titleLevel?: HeadingLevel; }; @@ -44,6 +51,7 @@ const CommentForm: FC = ({ parentId, saveComment, title, + titleAlignment, titleLevel = 2, ...props }) => { @@ -117,7 +125,7 @@ const CommentForm: FC = ({ {...props} > {title && ( - + {title} )} diff --git a/src/components/organisms/layout/summary.module.scss b/src/components/organisms/layout/summary.module.scss index 7e86dd2..62dfc0e 100644 --- a/src/components/organisms/layout/summary.module.scss +++ b/src/components/organisms/layout/summary.module.scss @@ -29,8 +29,11 @@ &:hover { .icon { - transform: scaleX(1.4); - transform-origin: left; + --icon-size: #{fun.convert-px(35)}; + + :global { + animation: pulse 1.5s ease-in-out 0.2s infinite; + } } } } diff --git a/src/components/templates/page/page-layout.module.scss b/src/components/templates/page/page-layout.module.scss index 83e5a80..c6b4e8d 100644 --- a/src/components/templates/page/page-layout.module.scss +++ b/src/components/templates/page/page-layout.module.scss @@ -86,7 +86,14 @@ &__section { grid-column: 2; - margin: var(--spacing-md) 0 0; + + &:first-child { + margin: var(--spacing-md) 0 0; + } + } + + &__no-comments { + text-align: center; } &__form { diff --git a/src/components/templates/page/page-layout.tsx b/src/components/templates/page/page-layout.tsx index f3f3ea8..2ff2084 100644 --- a/src/components/templates/page/page-layout.tsx +++ b/src/components/templates/page/page-layout.tsx @@ -1,6 +1,7 @@ import Heading from '@components/atoms/headings/heading'; import Notice, { type NoticeKind } from '@components/atoms/layout/notice'; import Sidebar from '@components/atoms/layout/sidebar'; +import { MetaData } from '@components/molecules/layout/meta'; import PageFooter, { type PageFooterProps, } from '@components/molecules/layout/page-footer'; @@ -118,10 +119,10 @@ const PageLayout: FC = ({ const isMounted = useIsMounted(bodyRef); const hasComments = Array.isArray(comments) && comments.length > 0; - const hasCommentsSection = hasComments || allowComments; - const articleModifier = hasCommentsSection - ? 'article--has-comments' - : 'article--no-comments'; + const articleModifier = + hasComments || allowComments + ? 'article--has-comments' + : 'article--no-comments'; const [status, setStatus] = useState('info'); const [statusMessage, setStatusMessage] = useState(''); @@ -175,6 +176,15 @@ const PageLayout: FC = ({ } }; + /** + * Check if meta properties are defined. + * + * @param {MetaData} meta - The metadata. + */ + const hasMeta = (meta: MetaData) => { + return Object.values(meta).every((value) => value === null); + }; + return ( = ({ {children} )} - + {footerMeta && hasMeta(footerMeta) && ( + + )} = ({ > {widgets} - {hasCommentsSection && ( -
- {hasComments && ( -
- {commentsTitle} + {allowComments && ( +
+
+ + {commentsTitle} + + {hasComments ? ( - ) : undefined + ) } saveComment={saveComment} /> -
- )} - {allowComments && ( -
- - ) : undefined - } - /> -
- )} + ) : ( +

+ {intl.formatMessage({ + defaultMessage: 'No comments.', + id: 'sBwfCy', + description: 'PageLayout: no comments text', + })} +

+ )} +
+
+ + ) + } + /> +
)}
diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx index 995e3a9..a0fb7fc 100644 --- a/src/pages/article/[slug].tsx +++ b/src/pages/article/[slug].tsx @@ -15,7 +15,10 @@ import { type Article, type Comment } from '@ts/types/app'; import { loadTranslation, type Messages } from '@utils/helpers/i18n'; import useAddPrismClassAttr from '@utils/hooks/use-add-prism-class-attr'; import useBreadcrumb from '@utils/hooks/use-breadcrumb'; -import usePrismPlugins, { PrismPlugin } from '@utils/hooks/use-prism-plugins'; +import usePrismPlugins, { + type PrismPlugin, +} from '@utils/hooks/use-prism-plugins'; +import useReadingTime from '@utils/hooks/use-reading-time'; import useSettings from '@utils/hooks/use-settings'; import { GetStaticPaths, GetStaticProps, NextPage } from 'next'; import Head from 'next/head'; @@ -38,7 +41,16 @@ type ArticlePageProps = { */ const ArticlePage: NextPage = ({ comments, post }) => { const { content, id, intro, meta, slug, title } = post; - const { author, commentsCount, cover, dates, seo, thematics, topics } = meta; + const { + author, + commentsCount, + cover, + dates, + seo, + thematics, + topics, + wordsCount, + } = meta; const { data } = useSWR(() => id, getPostComments, { fallbackData: comments, }); @@ -46,11 +58,13 @@ const ArticlePage: NextPage = ({ comments, post }) => { title, url: `/article/${slug}`, }); + const readingTime = useReadingTime(wordsCount || 0, true); const headerMeta: PageLayoutProps['headerMeta'] = { author: author?.name, publication: { date: dates.publication }, update: dates.update ? { date: dates.update } : undefined, + readingTime, thematics: thematics && thematics.map((thematic) => ( diff --git a/src/styles/pages/article.module.scss b/src/styles/pages/article.module.scss index a42c633..04acad9 100644 --- a/src/styles/pages/article.module.scss +++ b/src/styles/pages/article.module.scss @@ -31,6 +31,7 @@ @include mix.media("screen") { @include mix.dimensions("md") { width: min-content; + gap: var(--spacing-2xs); } } } -- cgit v1.2.3 From 7fd11d99f8a547e4acb89b9f9159b92e208dc90f Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Fri, 20 May 2022 22:12:43 +0200 Subject: chore: update images styles --- .../molecules/images/responsive-image.module.scss | 46 ++++++++++++++++++---- .../molecules/images/responsive-image.stories.tsx | 17 ++++++++ .../molecules/images/responsive-image.tsx | 14 ++++++- .../organisms/layout/overview.module.scss | 14 +++---- src/components/organisms/layout/overview.tsx | 2 +- src/styles/pages/article.module.scss | 2 +- src/styles/pages/partials/_article-media.scss | 17 ++++++-- src/styles/pages/partials/_article-wp-blocks.scss | 32 +++++++++------ 8 files changed, 111 insertions(+), 33 deletions(-) (limited to 'src/styles/pages/article.module.scss') diff --git a/src/components/molecules/images/responsive-image.module.scss b/src/components/molecules/images/responsive-image.module.scss index 3a4f283..8a1d51f 100644 --- a/src/components/molecules/images/responsive-image.module.scss +++ b/src/components/molecules/images/responsive-image.module.scss @@ -1,26 +1,49 @@ @use "@styles/abstracts/functions" as fun; +.caption { + margin: 0; + padding: fun.convert-px(4) var(--spacing-2xs); + background: var(--color-bg-secondary); + border: fun.convert-px(1) solid var(--color-border-light); + font-size: var(--font-size-sm); + font-weight: 500; +} + .wrapper { display: flex; flex-flow: column; width: fit-content; - margin: 0; + margin: 0 auto; position: relative; text-align: center; -} -.caption { - margin: 0; - padding: fun.convert-px(4) var(--spacing-2xs); - background: var(--color-bg-secondary); - border: fun.convert-px(1) solid var(--color-border-light); - font-weight: 500; + &--has-borders { + .caption { + margin-top: fun.convert-px(4); + } + } + + &--has-borders#{&}--has-link { + .link { + padding: fun.convert-px(4); + } + } + + &--has-borders#{&}--no-link { + padding: fun.convert-px(4); + border: fun.convert-px(1) solid var(--color-border); + box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) 0 + var(--color-shadow); + } } .link { display: flex; flex-flow: column; background: none; + border: fun.convert-px(1) solid var(--color-border); + box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) 0 + var(--color-shadow); text-decoration: none; .caption { @@ -29,6 +52,11 @@ &:hover, &:focus { + box-shadow: 0 0 fun.convert-px(2) 0 var(--color-shadow-light), + fun.convert-px(2) fun.convert-px(2) fun.convert-px(4) fun.convert-px(1) + var(--color-shadow-light), + fun.convert-px(4) fun.convert-px(4) fun.convert-px(8) fun.convert-px(2) + var(--color-shadow-light); transform: scale(var(--scale-up, 1.05)); } @@ -40,6 +68,8 @@ } &:active { + box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) + fun.convert-px(1) var(--color-shadow-light); transform: scale(var(--scale-down, 0.95)); .caption { diff --git a/src/components/molecules/images/responsive-image.stories.tsx b/src/components/molecules/images/responsive-image.stories.tsx index 35d9116..4294208 100644 --- a/src/components/molecules/images/responsive-image.stories.tsx +++ b/src/components/molecules/images/responsive-image.stories.tsx @@ -7,6 +7,9 @@ import ResponsiveImage from './responsive-image'; export default { title: 'Molecules/Images/ResponsiveImage', component: ResponsiveImage, + args: { + withBorders: false, + }, argTypes: { alt: { control: { @@ -75,6 +78,20 @@ export default { required: true, }, }, + withBorders: { + control: { + type: 'boolean', + }, + description: 'Add borders around the image.', + table: { + category: 'Styles', + defaultValue: { summary: false }, + }, + type: { + name: 'boolean', + required: false, + }, + }, }, } as ComponentMeta; diff --git a/src/components/molecules/images/responsive-image.tsx b/src/components/molecules/images/responsive-image.tsx index d07dd6c..4541df8 100644 --- a/src/components/molecules/images/responsive-image.tsx +++ b/src/components/molecules/images/responsive-image.tsx @@ -31,6 +31,10 @@ export type ResponsiveImageProps = Omit< * The image width. */ width: number | string; + /** + * Wrap the image with borders. + */ + withBorders?: boolean; }; /** @@ -45,10 +49,18 @@ const ResponsiveImage: FC = ({ layout, objectFit, target, + withBorders, ...props }) => { + const bordersModifier = withBorders + ? 'wrapper--has-borders' + : 'wrapper--no-borders'; + const linkModifier = target ? 'wrapper--has-link' : 'wrapper--no-link'; + return ( -
+
{target ? ( = ({ className = '', cover, meta }) => {
{cover && ( )} diff --git a/src/styles/pages/article.module.scss b/src/styles/pages/article.module.scss index 04acad9..a5299fe 100644 --- a/src/styles/pages/article.module.scss +++ b/src/styles/pages/article.module.scss @@ -11,7 +11,7 @@ margin-right: var(--spacing-2xs); padding: var(--spacing-2xs) var(--spacing-xs); - &__icon { + figure { max-width: fun.convert-px(22); margin-right: var(--spacing-2xs); } diff --git a/src/styles/pages/partials/_article-media.scss b/src/styles/pages/partials/_article-media.scss index 8359881..0cd3654 100644 --- a/src/styles/pages/partials/_article-media.scss +++ b/src/styles/pages/partials/_article-media.scss @@ -1,11 +1,20 @@ +@use "@styles/abstracts/functions" as fun; + @mixin styles { figure { - margin: var(--spacing-md) 0; + display: flex; + flex-flow: column; + width: fit-content; + margin: 0 auto; + position: relative; + text-align: center; } figcaption { - margin-top: var(--spacing-xs); - font-size: var(--font-size-sm); - text-align: center; + margin: 0; + padding: fun.convert-px(4) var(--spacing-2xs); + background: var(--color-bg-secondary); + border: fun.convert-px(1) solid var(--color-border-light); + font-weight: 500; } } diff --git a/src/styles/pages/partials/_article-wp-blocks.scss b/src/styles/pages/partials/_article-wp-blocks.scss index f75a4d5..33e59a1 100644 --- a/src/styles/pages/partials/_article-wp-blocks.scss +++ b/src/styles/pages/partials/_article-wp-blocks.scss @@ -62,6 +62,24 @@ } } + .wp-block-image { + display: flex; + flex-flow: column; + width: fit-content; + margin: 0 auto; + padding: fun.convert-px(4); + position: relative; + border: fun.convert-px(1) solid var(--color-border); + box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) 0 + var(--color-shadow); + text-align: center; + + figcaption { + margin-top: fun.convert-px(4); + font-size: var(--font-size-sm); + } + } + .wp-block-gallery { display: grid; grid-template-columns: minmax(0, 1fr); @@ -142,18 +160,10 @@ } } } - } - .wp-block-image { - img { - display: block; - margin: auto; - box-shadow: 0 0 fun.convert-px(1) 0 var(--color-shadow), - fun.convert-px(2) fun.convert-px(2) fun.convert-px(2) 0 - var(--color-shadow-light), - fun.convert-px(3) fun.convert-px(3) fun.convert-px(6) 0 - var(--color-shadow-light); - text-align: center; + .wp-block-image img { + height: 100%; + object-fit: cover; } } -- cgit v1.2.3