Skip to content

Commit a2e4aff

Browse files
wisnietypeofweb
andauthored
feat: create a documentation layout for desktops & tablets (#23)
* feat: create a documentation layout for desktops & tablets * feat: create initial layout for mobile * create layout for tablet & move things to css variables & implement some transitions * code review * Fix build Co-authored-by: Michal Miszczyszyn <[email protected]>
1 parent 3f04ed4 commit a2e4aff

20 files changed

+636
-309
lines changed

docs/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"prettier": "2.3.2",
2424
"react": "17.0.2",
2525
"react-dom": "17.0.2",
26+
"sass": "^1.35.1",
2627
"rehype-autolink-headings": "5.1.0",
2728
"rehype-document": "5.1.0",
2829
"rehype-format": "3.1.0",

docs/public/icons/chevron-down.svg

+1
Loading

docs/public/icons/chevron-up.svg

+1
Loading

docs/public/icons/menu.svg

+1
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
.accordionWrapper {
2+
margin-bottom: 0.45rem;
3+
}
4+
5+
.accordionHeader {
6+
display: flex;
7+
justify-content: space-between;
8+
align-items: center;
9+
cursor: pointer;
10+
11+
&:hover {
12+
.accordionHeading {
13+
color: var(--active);
14+
}
15+
}
16+
}
17+
18+
.accordionHeading {
19+
font-size: 0.875rem;
20+
color: var(--gray-900);
21+
font-weight: 600;
22+
text-transform: uppercase;
23+
transition: 300ms color ease-in;
24+
25+
&Active {
26+
color: var(--active);
27+
}
28+
}
29+
30+
.accordionChevron {
31+
transform: rotate(0);
32+
33+
&Expanded {
34+
transform: rotate(180deg);
35+
}
36+
}
37+
38+
.accordionSectionList {
39+
padding: 0 0.75rem;
40+
list-style: none;
41+
}
42+
43+
.accordionSection {
44+
padding: 0.125rem 0;
45+
}
46+
47+
.accordionSectionAnchor {
48+
font-size: 0.75rem;
49+
color: var(--gray-700);
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { memo, useCallback, useState } from 'react';
2+
import styles from './Accordion.module.scss';
3+
4+
type AccordionProps = {
5+
heading: string;
6+
isActive: boolean;
7+
sections?: string[];
8+
};
9+
export const Accordion = memo<AccordionProps>(({ heading, isActive, sections }) => {
10+
const [expanded, setExpanded] = useState<boolean>(false);
11+
const hasSections = sections && sections.length > 0;
12+
const toggleExpanded = useCallback(() => setExpanded((prev) => !prev), []);
13+
return (
14+
<div className={styles.accordionWrapper}>
15+
<header className={styles.accordionHeader} onClick={toggleExpanded}>
16+
<h3 className={`${styles.accordionHeading} ${isActive ? styles.accordionHeadingActive : ''}`}>{heading}</h3>
17+
{hasSections && (
18+
<img
19+
className={`${styles.accordionChevron} ${expanded ? styles.accordionChevronExpanded : ''}`}
20+
src="/icons/chevron-down.svg"
21+
alt={expanded ? 'Accordion expanded' : 'Accordion collapsed'}
22+
/>
23+
)}
24+
</header>
25+
{hasSections && expanded && (
26+
<ul className={styles.accordionSectionList}>
27+
{sections?.map((section) => (
28+
<li className={styles.accordionSection} key={section}>
29+
<a className={styles.accordionSectionAnchor} href="#">
30+
{section}
31+
</a>
32+
</li>
33+
))}
34+
</ul>
35+
)}
36+
</div>
37+
);
38+
});
39+
Accordion.displayName = 'Accordion';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
.header {
2+
position: sticky;
3+
top: 0;
4+
width: 100%;
5+
padding: 1.35rem 1.25rem;
6+
background-color: var(--nav-background);
7+
min-height: var(--header-height);
8+
z-index: 100;
9+
display: flex;
10+
justify-content: flex-start;
11+
align-items: center;
12+
13+
@media screen and (min-width: 55rem) {
14+
padding: 0;
15+
}
16+
}
17+
18+
.srOnly {
19+
position: absolute;
20+
left: -10000px;
21+
top: auto;
22+
width: 1px;
23+
height: 1px;
24+
overflow: hidden;
25+
}
26+
27+
.toggleButton {
28+
background-color: transparent;
29+
border: none;
30+
@media screen and (min-width: 55rem) {
31+
display: flex;
32+
justify-content: center;
33+
align-items: center;
34+
width: var(--reference-width);
35+
}
36+
}
37+
38+
.menuIcon {
39+
cursor: pointer;
40+
filter: invert(100%);
41+
}
42+
43+
.nav {
44+
display: none;
45+
46+
@media screen and (min-width: 75rem) {
47+
display: block;
48+
width: 100%;
49+
max-width: calc(100% - var(--reference-width) - var(--table-of-contents-width));
50+
margin: 0 var(--table-of-contents-width) 0 auto;
51+
background-color: var(--nav-background);
52+
}
53+
}
54+
55+
.navList {
56+
display: flex;
57+
justify-content: flex-end;
58+
list-style: none;
59+
}
60+
61+
.navItem {
62+
padding: 0.25rem 1rem;
63+
}
64+
65+
.navAnchor {
66+
font-size: 1rem;
67+
font-weight: 400;
68+
color: var(--white);
69+
}

docs/src/components/Header/Header.tsx

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { memo } from 'react';
2+
3+
import styles from './Header.module.scss';
4+
5+
type HeaderProps = {
6+
toggleMenuOpened: () => void;
7+
};
8+
9+
export const Header = memo<HeaderProps>(({ toggleMenuOpened }) => {
10+
return (
11+
<header className={styles.header}>
12+
<button className={styles.toggleButton} onClick={toggleMenuOpened}>
13+
<span className={styles.srOnly}>Toggle reference sidebar</span>
14+
<img className={styles.menuIcon} src="/icons/menu.svg" alt="Menu" />
15+
</button>
16+
<nav className={styles.nav}>
17+
<ul className={styles.navList}>
18+
<li className={styles.navItem}>
19+
<a className={styles.navAnchor} href="#">
20+
Documentation
21+
</a>
22+
</li>
23+
<li className={styles.navItem}>
24+
<a className={styles.navAnchor} href="#">
25+
API
26+
</a>
27+
</li>
28+
<li className={styles.navItem}>
29+
<a className={styles.navAnchor} href="#">
30+
Github
31+
</a>
32+
</li>
33+
</ul>
34+
</nav>
35+
</header>
36+
);
37+
});
38+
Header.displayName = 'Header';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
.reference {
2+
position: fixed;
3+
top: 0;
4+
width: 100%;
5+
height: 100vh;
6+
background-color: var(--section-background);
7+
padding: 1.25rem;
8+
padding-top: calc(2rem + var(--header-height));
9+
transform: translateX(-100%);
10+
transition: 200ms transform ease-in;
11+
12+
&Opened {
13+
transform: translateX(0);
14+
}
15+
16+
@media screen and (min-width: 55rem) {
17+
// padding-top: 2rem;
18+
// height: auto;
19+
// max-height: calc(100vh - var(--header-height));
20+
// transform: translateX(0);
21+
// position: sticky;
22+
// top: var(--header-height);
23+
width: var(--reference-width);
24+
// background-color: var(--section-background);
25+
// flex-shrink: 0;
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { memo } from 'react';
2+
import { Accordion } from '../Accordion/Accordion';
3+
import styles from './Reference.module.scss';
4+
5+
export type ReferenceType = {
6+
heading: string;
7+
sections?: string[];
8+
}[];
9+
10+
type ReferenceProps = {
11+
reference: ReferenceType;
12+
menuOpened: boolean;
13+
};
14+
15+
export const Reference = memo<ReferenceProps>(({ reference, menuOpened }) => (
16+
<section className={`${styles.reference} ${menuOpened ? styles.referenceOpened : ''}`}>
17+
{reference.map(({ heading, sections }) => (
18+
// isActive should be derived from the current url
19+
<Accordion isActive={false} key={heading} heading={heading} sections={sections} />
20+
))}
21+
</section>
22+
));
23+
Reference.displayName = 'Reference';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
.tableOfContentsWrapper {
2+
display: none;
3+
4+
@media screen and (min-width: 75rem) {
5+
display: block;
6+
width: var(--table-of-contents-width);
7+
flex-shrink: 0;
8+
padding: 3rem 2rem;
9+
}
10+
}
11+
12+
.list {
13+
display: flex;
14+
flex-direction: column;
15+
list-style: none;
16+
}
17+
18+
.listItem {
19+
position: relative;
20+
padding: 0.25rem 1rem;
21+
}
22+
23+
.anchor {
24+
position: relative;
25+
font-size: 0.875rem;
26+
font-weight: 400;
27+
text-decoration: none;
28+
color: var(--text-700);
29+
30+
&::before {
31+
content: '';
32+
position: absolute;
33+
left: -1.5rem;
34+
top: 50%;
35+
width: 0.5rem;
36+
height: 0.5rem;
37+
transform: translateY(-50%);
38+
border: 1px solid var(--gray-500);
39+
border-radius: 50%;
40+
}
41+
42+
&Active {
43+
font-weight: 600;
44+
color: var(--active);
45+
46+
&::before {
47+
border: 1px solid var(--active);
48+
background-color: var(--active);
49+
}
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { memo } from 'react';
2+
import styles from './TableOfContents.module.scss';
3+
4+
type TableOfContentsProps = {
5+
headings: string[];
6+
activeHeading: string;
7+
};
8+
export const TableOfContents = memo<TableOfContentsProps>(({ headings, activeHeading }) => {
9+
return (
10+
<section className={styles.tableOfContentsWrapper}>
11+
<ol className={styles.list}>
12+
{headings.map((heading) => (
13+
<li className={styles.listItem} key={heading}>
14+
<a className={`${styles.anchor} ${heading === activeHeading ? styles.anchorActive : ''}`}>{heading}</a>
15+
</li>
16+
))}
17+
</ol>
18+
</section>
19+
);
20+
});
21+
TableOfContents.displayName = 'TableOfContents';

docs/src/pages/_app.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { AppComponent } from 'next/dist/next-server/lib/router/router';
2-
import '../styles/globals.css';
2+
3+
import '../styles/globals.scss';
34
import '../styles/prism.css';
45

56
const App: AppComponent = ({ Component, pageProps }) => {

0 commit comments

Comments
 (0)