aboutsummaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/atoms/lists/list.module.scss16
-rw-r--r--src/components/atoms/lists/list.stories.tsx13
-rw-r--r--src/components/atoms/lists/list.tsx4
-rw-r--r--src/components/molecules/layout/card.module.scss3
-rw-r--r--src/components/molecules/layout/card.tsx2
-rw-r--r--src/components/molecules/layout/page-header.tsx2
-rw-r--r--src/components/molecules/nav/nav.stories.tsx26
-rw-r--r--src/components/molecules/nav/nav.tsx7
-rw-r--r--src/components/organisms/layout/cards-list.module.scss4
-rw-r--r--src/components/organisms/layout/cards-list.stories.tsx13
-rw-r--r--src/components/organisms/layout/cards-list.tsx8
-rw-r--r--src/components/templates/layout/layout.module.scss9
-rw-r--r--src/components/templates/layout/layout.stories.tsx10
-rw-r--r--src/components/templates/layout/layout.tsx107
14 files changed, 183 insertions, 41 deletions
diff --git a/src/components/atoms/lists/list.module.scss b/src/components/atoms/lists/list.module.scss
index df3b49c..f647072 100644
--- a/src/components/atoms/lists/list.module.scss
+++ b/src/components/atoms/lists/list.module.scss
@@ -1,3 +1,5 @@
+@use "@styles/abstracts/placeholders";
+
.list {
margin: 0;
@@ -36,4 +38,18 @@
margin-bottom: var(--spacing-2xs);
}
}
+
+ &--flex {
+ @extend %reset-list;
+
+ display: flex;
+ flex-flow: row wrap;
+ gap: var(--spacing-sm);
+ }
+
+ &--flex &--flex {
+ display: initial;
+ position: relative;
+ top: var(--spacing-2xs);
+ }
}
diff --git a/src/components/atoms/lists/list.stories.tsx b/src/components/atoms/lists/list.stories.tsx
index 3a80962..54fdd3a 100644
--- a/src/components/atoms/lists/list.stories.tsx
+++ b/src/components/atoms/lists/list.stories.tsx
@@ -39,8 +39,8 @@ export default {
control: {
type: 'select',
},
- description: 'The list kind: ordered or unordered.',
- options: ['ordered', 'unordered'],
+ description: 'The list kind: flex, ordered or unordered.',
+ options: ['flex', 'ordered', 'unordered'],
table: {
category: 'Options',
defaultValue: { summary: 'unordered' },
@@ -72,6 +72,15 @@ const items: ListItem[] = [
];
/**
+ * List Stories - Flex list
+ */
+export const Flex = Template.bind({});
+Flex.args = {
+ items,
+ kind: 'flex',
+};
+
+/**
* List Stories - Ordered list
*/
export const Ordered = Template.bind({});
diff --git a/src/components/atoms/lists/list.tsx b/src/components/atoms/lists/list.tsx
index 6726802..711ade1 100644
--- a/src/components/atoms/lists/list.tsx
+++ b/src/components/atoms/lists/list.tsx
@@ -30,9 +30,9 @@ export type ListProps = {
*/
itemsClassName?: string;
/**
- * The list kind (ordered or unordered).
+ * The list kind.
*/
- kind?: 'ordered' | 'unordered';
+ kind?: 'ordered' | 'unordered' | 'flex';
/**
* Set margin between list items. Default: true.
*/
diff --git a/src/components/molecules/layout/card.module.scss b/src/components/molecules/layout/card.module.scss
index 85c319a..d5b9836 100644
--- a/src/components/molecules/layout/card.module.scss
+++ b/src/components/molecules/layout/card.module.scss
@@ -19,7 +19,8 @@
.cover {
align-self: flex-start;
- max-height: fun.convert-px(150);
+ place-content: center;
+ height: fun.convert-px(150);
margin: auto;
border-bottom: fun.convert-px(1) solid var(--color-border);
}
diff --git a/src/components/molecules/layout/card.tsx b/src/components/molecules/layout/card.tsx
index 15927e9..89f100e 100644
--- a/src/components/molecules/layout/card.tsx
+++ b/src/components/molecules/layout/card.tsx
@@ -93,7 +93,7 @@ const Card: FC<CardProps> = ({
{title}
</Heading>
</header>
- {tagline && <div className={styles.tagline}>{tagline}</div>}
+ <div className={styles.tagline}>{tagline}</div>
{meta && (
<footer className={styles.footer}>
<DescriptionList
diff --git a/src/components/molecules/layout/page-header.tsx b/src/components/molecules/layout/page-header.tsx
index 174e246..1663085 100644
--- a/src/components/molecules/layout/page-header.tsx
+++ b/src/components/molecules/layout/page-header.tsx
@@ -11,7 +11,7 @@ export type PageHeaderProps = {
/**
* The page introduction.
*/
- intro?: string;
+ intro?: string | JSX.Element;
/**
* The page metadata.
*/
diff --git a/src/components/molecules/nav/nav.stories.tsx b/src/components/molecules/nav/nav.stories.tsx
index 25455fd..5cef5f0 100644
--- a/src/components/molecules/nav/nav.stories.tsx
+++ b/src/components/molecules/nav/nav.stories.tsx
@@ -11,6 +11,19 @@ export default {
title: 'Molecules/Navigation/Nav',
component: NavComponent,
argTypes: {
+ 'aria-label': {
+ control: {
+ type: 'text',
+ },
+ description: 'An accessible name for the navigation.',
+ table: {
+ category: 'Accessibility',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
className: {
control: {
type: 'text',
@@ -46,6 +59,19 @@ export default {
required: true,
},
},
+ listClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the navigation list.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
},
decorators: [
(Story) => (
diff --git a/src/components/molecules/nav/nav.tsx b/src/components/molecules/nav/nav.tsx
index 2666ea2..581f813 100644
--- a/src/components/molecules/nav/nav.tsx
+++ b/src/components/molecules/nav/nav.tsx
@@ -24,6 +24,10 @@ export type NavItem = {
export type NavProps = {
/**
+ * An accessible name.
+ */
+ 'aria-label'?: string;
+ /**
* Set additional classnames to the navigation wrapper.
*/
className?: string;
@@ -51,6 +55,7 @@ const Nav: FC<NavProps> = ({
items,
kind,
listClassName = '',
+ ...props
}) => {
const kindClass = `nav--${kind}`;
@@ -71,7 +76,7 @@ const Nav: FC<NavProps> = ({
};
return (
- <nav className={`${styles[kindClass]} ${className}`}>
+ <nav className={`${styles[kindClass]} ${className}`} {...props}>
<ul className={`${styles.nav__list} ${listClassName}`}>{getItems()}</ul>
</nav>
);
diff --git a/src/components/organisms/layout/cards-list.module.scss b/src/components/organisms/layout/cards-list.module.scss
index 9fe428c..2763585 100644
--- a/src/components/organisms/layout/cards-list.module.scss
+++ b/src/components/organisms/layout/cards-list.module.scss
@@ -1,12 +1,10 @@
@use "@styles/abstracts/placeholders";
.wrapper {
- --card-width: 30ch;
-
display: grid;
grid-template-columns: repeat(
auto-fit,
- min(calc(100vw - (var(--spacing-md) * 2)), var(--card-width))
+ min(calc(100vw - (var(--spacing-md) * 2)), var(--card-width, 30ch))
);
gap: var(--spacing-sm);
place-content: center;
diff --git a/src/components/organisms/layout/cards-list.stories.tsx b/src/components/organisms/layout/cards-list.stories.tsx
index 7ff4365..fe0ebfd 100644
--- a/src/components/organisms/layout/cards-list.stories.tsx
+++ b/src/components/organisms/layout/cards-list.stories.tsx
@@ -12,6 +12,19 @@ export default {
kind: 'unordered',
},
argTypes: {
+ className: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the list wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
coverFit: {
control: {
type: 'select',
diff --git a/src/components/organisms/layout/cards-list.tsx b/src/components/organisms/layout/cards-list.tsx
index 33ffe23..1558d7c 100644
--- a/src/components/organisms/layout/cards-list.tsx
+++ b/src/components/organisms/layout/cards-list.tsx
@@ -15,6 +15,10 @@ export type CardsListItem = Omit<
export type CardsListProps = {
/**
+ * Set additional classnames to the list wrapper.
+ */
+ className?: string;
+ /**
* The cover fit.
*/
coverFit?: CardProps['coverFit'];
@@ -38,6 +42,7 @@ export type CardsListProps = {
* Return a list of Card components.
*/
const CardsList: FC<CardsListProps> = ({
+ className = '',
coverFit,
items,
kind = 'unordered',
@@ -70,9 +75,10 @@ const CardsList: FC<CardsListProps> = ({
return (
<List
+ kind="flex"
items={getCards(items)}
withMargin={false}
- className={`${styles.wrapper} ${styles[kindModifier]}`}
+ className={`${styles.wrapper} ${styles[kindModifier]} ${className}`}
/>
);
};
diff --git a/src/components/templates/layout/layout.module.scss b/src/components/templates/layout/layout.module.scss
index 3533257..806d2d7 100644
--- a/src/components/templates/layout/layout.module.scss
+++ b/src/components/templates/layout/layout.module.scss
@@ -1,15 +1,6 @@
@use "@styles/abstracts/functions" as fun;
@use "@styles/abstracts/mixins" as mix;
-:global {
- #__next {
- flex: 1;
- display: flex;
- flex-flow: column nowrap;
- min-height: 100vh;
- }
-}
-
.header {
border-bottom: fun.convert-px(3) solid var(--color-border-light);
}
diff --git a/src/components/templates/layout/layout.stories.tsx b/src/components/templates/layout/layout.stories.tsx
index f3579e3..2415412 100644
--- a/src/components/templates/layout/layout.stories.tsx
+++ b/src/components/templates/layout/layout.stories.tsx
@@ -36,7 +36,15 @@ export default {
decorators: [
(Story) => (
<IntlProvider locale="en">
- <div id="__next">
+ <div
+ id="__next"
+ style={{
+ flex: 1,
+ display: 'flex',
+ flexFlow: 'column nowrap',
+ minHeight: '100vh',
+ }}
+ >
<Story />
</div>
</IntlProvider>
diff --git a/src/components/templates/layout/layout.tsx b/src/components/templates/layout/layout.tsx
index 601ced4..e1be1af 100644
--- a/src/components/templates/layout/layout.tsx
+++ b/src/components/templates/layout/layout.tsx
@@ -1,4 +1,3 @@
-import photo from '@assets/images/armand-philippot.jpg';
import ButtonLink from '@components/atoms/buttons/button-link';
import Career from '@components/atoms/icons/career';
import CCBySA from '@components/atoms/icons/cc-by-sa';
@@ -8,13 +7,19 @@ import Home from '@components/atoms/icons/home';
import PostsStack from '@components/atoms/icons/posts-stack';
import Main from '@components/atoms/layout/main';
import NoScript from '@components/atoms/layout/no-script';
-import Footer from '@components/organisms/layout/footer';
-import Header, { HeaderProps } from '@components/organisms/layout/header';
-import { settings } from '@utils/config';
+import Footer, { FooterProps } from '@components/organisms/layout/footer';
+import Header, { type HeaderProps } from '@components/organisms/layout/header';
+import useSettings from '@utils/hooks/use-settings';
+import Script from 'next/script';
import { FC, ReactNode } from 'react';
import { useIntl } from 'react-intl';
+import { Person, SearchAction, WebSite, WithContext } from 'schema-dts';
import styles from './layout.module.scss';
+export type QueryAction = SearchAction & {
+ 'query-input': string;
+};
+
export type LayoutProps = Pick<HeaderProps, 'isHome'> & {
/**
* The layout main content.
@@ -33,6 +38,9 @@ export type LayoutProps = Pick<HeaderProps, 'isHome'> & {
*/
const Layout: FC<LayoutProps> = ({ children, isHome, ...props }) => {
const intl = useIntl();
+ const { website } = useSettings();
+ const { baseline, copyright, locales, name, picture, url } = website;
+
const skipToContent = intl.formatMessage({
defaultMessage: 'Skip to content',
description: 'Layout: Skip to content link',
@@ -45,12 +53,12 @@ const Layout: FC<LayoutProps> = ({ children, isHome, ...props }) => {
id: '7jVUT6',
});
- const copyright = {
+ const copyrightData = {
dates: {
- start: settings.copyright.startYear,
- end: settings.copyright.endYear,
+ start: copyright.start,
+ end: copyright.end,
},
- owner: settings.name,
+ owner: name,
icon: <CCBySA />,
};
@@ -80,21 +88,77 @@ const Layout: FC<LayoutProps> = ({ children, isHome, ...props }) => {
id: 'AE4kCD',
});
- const nav: HeaderProps['nav'] = [
- { id: 'home', label: homeLabel, href: '#', logo: <Home /> },
- { id: 'blog', label: blogLabel, href: '#', logo: <PostsStack /> },
+ const mainNav: HeaderProps['nav'] = [
+ { id: 'home', label: homeLabel, href: '/', logo: <Home /> },
+ { id: 'blog', label: blogLabel, href: '/blog', logo: <PostsStack /> },
{
id: 'projects',
label: projectsLabel,
- href: '#',
+ href: '/projets',
logo: <ComputerScreen />,
},
- { id: 'cv', label: cvLabel, href: '#', logo: <Career /> },
- { id: 'contact', label: contactLabel, href: '#', logo: <Envelop /> },
+ { id: 'cv', label: cvLabel, href: '/cv', logo: <Career /> },
+ { id: 'contact', label: contactLabel, href: '/contact', logo: <Envelop /> },
+ ];
+
+ const legalNoticeLabel = intl.formatMessage({
+ defaultMessage: 'Legal notice',
+ description: 'Layout: Legal notice label',
+ id: 'nwbzKm',
+ });
+
+ const footerNav: FooterProps['navItems'] = [
+ { id: 'legal-notice', label: legalNoticeLabel, href: '/mentions-legales' },
];
+ const searchActionSchema: QueryAction = {
+ '@type': 'SearchAction',
+ target: {
+ '@type': 'EntryPoint',
+ urlTemplate: `${url}/recherche?s={search_term_string}`,
+ },
+ query: 'required',
+ 'query-input': 'required name=search_term_string',
+ };
+
+ const schemaJsonLd: WithContext<WebSite> = {
+ '@context': 'https://schema.org',
+ '@id': `${url}`,
+ '@type': 'WebSite',
+ name: name,
+ description: baseline,
+ url: url,
+ author: { '@id': `${url}/#branding` },
+ copyrightYear: Number(copyright.start),
+ creator: { '@id': `${url}/#branding` },
+ editor: { '@id': `${url}/#branding` },
+ inLanguage: locales.default,
+ potentialAction: searchActionSchema,
+ };
+
+ const brandingSchema: WithContext<Person> = {
+ '@context': 'https://schema.org',
+ '@type': 'Person',
+ '@id': `${url}/#branding`,
+ name: name,
+ url: url,
+ jobTitle: baseline,
+ image: picture.src,
+ subjectOf: { '@id': `${url}` },
+ };
+
return (
<>
+ <Script
+ id="schema-layout"
+ type="application/ld+json"
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
+ ></Script>
+ <Script
+ id="schema-branding"
+ type="application/ld+json"
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(brandingSchema) }}
+ />
<noscript>
<div className={styles['noscript-spacing']}></div>
</noscript>
@@ -103,10 +167,10 @@ const Layout: FC<LayoutProps> = ({ children, isHome, ...props }) => {
{skipToContent}
</ButtonLink>
<Header
- title={settings.name}
- baseline={settings.baseline.fr}
- photo={photo.src}
- nav={nav}
+ title={name}
+ baseline={baseline}
+ photo={picture}
+ nav={mainNav}
isHome={isHome}
className={styles.header}
withLink={true}
@@ -114,7 +178,12 @@ const Layout: FC<LayoutProps> = ({ children, isHome, ...props }) => {
<Main id="main" className={styles.main}>
<article {...props}>{children}</article>
</Main>
- <Footer copyright={copyright} topId="top" className={styles.footer} />
+ <Footer
+ copyright={copyrightData}
+ navItems={footerNav}
+ topId="top"
+ className={styles.footer}
+ />
<noscript>
<NoScript message={noScript} position="top" />
</noscript>