Skip to content

Commit 70624a0

Browse files
committed
feat(ui): 💄 update footer design
1 parent ff8e9f4 commit 70624a0

File tree

10 files changed

+228
-14
lines changed

10 files changed

+228
-14
lines changed

crowdsec-docs/src/css/custom.css

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,6 @@ blockquote {
264264
--ifm-navbar-link-hover-color: rgb(var(--primary));
265265
}
266266

267-
.footer,
268267
.navbar,
269268
.navbar-sidebar {
270269
@apply bg-card text-foreground border-border/80;
@@ -273,19 +272,7 @@ blockquote {
273272
}
274273

275274
.navbar {
276-
@apply border border-b border-solid;
277-
}
278-
279-
.footer {
280-
@apply border-0 border-t border-solid;
281-
}
282-
283-
.footer__copyright {
284-
@apply text-foreground/80 text-sm;
285-
}
286-
287-
.footer a {
288-
@apply text-foreground hover:text-primary;
275+
@apply border border-b border-solid;
289276
}
290277

291278
/** Patch some colors **/
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { Props } from "@theme/Footer/Copyright";
2+
import React from "react";
3+
4+
export default function FooterCopyright({ copyright }: Readonly<Props>): React.JSX.Element {
5+
return (
6+
<div className="mt-8 border-0 border-t border-border/40 border-solid pt-4">
7+
<p className="text-sm/6 text-foreground/50 text-left">{copyright}, Inc. All rights reserved.</p>
8+
</div>
9+
);
10+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { Props } from "@theme/Footer/Layout";
2+
import React from "react";
3+
4+
export default function FooterLayout({ links, logo, copyright }: Readonly<Props>): React.JSX.Element {
5+
return (
6+
<footer className="bg-card border-0 border-t border-border/80 border-solid">
7+
<div className="mx-auto max-w-7xl px-6 py-8 lg:px-8 space-y-8">
8+
<div className="flex flex-row items-center gap-6">
9+
<img alt="CrowdSec Logo" src="/img/crowdsec_logo.png" className="h-10" />
10+
<div className="flex flex-col items-start gap-0">
11+
<h3 className="mb-0">CrowdSec</h3>
12+
<p className="text-balance text-sm/6 text-foreground/80 mb-0">Safer together.</p>
13+
</div>
14+
</div>
15+
16+
{links}
17+
{(logo || copyright) && (
18+
<div className="footer__bottom text--center">
19+
{logo && <div className="margin-bottom--sm">{logo}</div>}
20+
{copyright}
21+
</div>
22+
)}
23+
</div>
24+
</footer>
25+
);
26+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import Link from "@docusaurus/Link";
2+
import useBaseUrl from "@docusaurus/useBaseUrl";
3+
import type { Props } from "@theme/Footer/LinkItem";
4+
import clsx from "clsx";
5+
import React from "react";
6+
7+
export default function FooterLinkItem({ item }: Readonly<Props>): React.JSX.Element {
8+
const { to, href, label, prependBaseUrlToHref, className, ...props } = item;
9+
const toUrl = useBaseUrl(to);
10+
const normalizedHref = useBaseUrl(href, { forcePrependBaseUrl: true });
11+
12+
return (
13+
<Link
14+
className={clsx("text-sm/6 text-foreground/80 hover:text-primary", className)}
15+
{...(href
16+
? {
17+
href: prependBaseUrlToHref ? normalizedHref : href,
18+
}
19+
: {
20+
to: toUrl,
21+
})}
22+
{...props}
23+
>
24+
{label}
25+
</Link>
26+
);
27+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { ThemeClassNames } from "@docusaurus/theme-common";
2+
import LinkItem from "@theme/Footer/LinkItem";
3+
import type { Props } from "@theme/Footer/Links/MultiColumn";
4+
import clsx from "clsx";
5+
import React from "react";
6+
7+
type ColumnType = Props["columns"][number];
8+
type ColumnItemType = ColumnType["items"][number];
9+
10+
const ColumnLinkItem = ({ item }: { item: ColumnItemType }): React.JSX.Element => {
11+
return item.html ? (
12+
<li
13+
className={clsx("footer__item", item.className)}
14+
// Developer provided the HTML, so assume it's safe.
15+
// biome-ignore lint/security/noDangerouslySetInnerHtml: we trust the content
16+
dangerouslySetInnerHTML={{ __html: item.html }}
17+
/>
18+
) : (
19+
<li key={item.href ?? item.to} className="footer__item">
20+
<LinkItem item={item} />
21+
</li>
22+
);
23+
};
24+
25+
const Column = ({ column }: Readonly<{ column: ColumnType }>): React.JSX.Element => {
26+
return (
27+
<div className={clsx(ThemeClassNames.layout.footer.column, "col footer__col", column.className)}>
28+
<div className="footer__title">{column.title}</div>
29+
<ul className="footer__items clean-list">
30+
{column.items.map((item, i) => (
31+
// biome-ignore lint/suspicious/noArrayIndexKey: We use the index as a key here because the columns are static and do not change.
32+
<ColumnLinkItem key={i} item={item} />
33+
))}
34+
</ul>
35+
</div>
36+
);
37+
};
38+
39+
export default function FooterLinksMultiColumn({ columns }: Readonly<Props>): React.JSX.Element {
40+
return (
41+
<div className="row footer__links">
42+
{columns.map((column, i) => (
43+
// biome-ignore lint/suspicious/noArrayIndexKey: We use the index as a key here because the columns are static and do not change.
44+
<Column key={i} column={column} />
45+
))}
46+
</div>
47+
);
48+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import LinkItem from "@theme/Footer/LinkItem";
2+
import type { Props } from "@theme/Footer/Links/Simple";
3+
import clsx from "clsx";
4+
import React from "react";
5+
6+
function Separator() {
7+
return <span className="footer__link-separator">·</span>;
8+
}
9+
10+
function SimpleLinkItem({ item }: { item: Props["links"][number] }) {
11+
return item.html ? (
12+
<span
13+
className={clsx("footer__link-item", item.className)}
14+
// Developer provided the HTML, so assume it's safe.
15+
// biome-ignore lint/security/noDangerouslySetInnerHtml: we trust the content
16+
dangerouslySetInnerHTML={{ __html: item.html }}
17+
/>
18+
) : (
19+
<LinkItem item={item} />
20+
);
21+
}
22+
23+
export default function FooterLinksSimple({ links }: Readonly<Props>): React.JSX.Element {
24+
return (
25+
<div className="footer__links text--center">
26+
<div className="footer__links">
27+
{links.map((item, i) => (
28+
// biome-ignore lint/suspicious/noArrayIndexKey: We use the index as a key here because the links are static and do not change.
29+
<React.Fragment key={i}>
30+
<SimpleLinkItem item={item} />
31+
{links.length !== i + 1 && <Separator />}
32+
</React.Fragment>
33+
))}
34+
</div>
35+
</div>
36+
);
37+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { isMultiColumnFooterLinks } from "@docusaurus/theme-common";
2+
import type { Props } from "@theme/Footer/Links";
3+
import FooterLinksMultiColumn from "@theme/Footer/Links/MultiColumn";
4+
import FooterLinksSimple from "@theme/Footer/Links/Simple";
5+
import React from "react";
6+
7+
export default function FooterLinks({ links }: Props): React.JSX.Element {
8+
return isMultiColumnFooterLinks(links) ? <FooterLinksMultiColumn columns={links} /> : <FooterLinksSimple links={links} />;
9+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import Link from "@docusaurus/Link";
2+
import { useBaseUrlUtils } from "@docusaurus/useBaseUrl";
3+
import type { Props } from "@theme/Footer/Logo";
4+
import ThemedImage from "@theme/ThemedImage";
5+
import clsx from "clsx";
6+
import React, { type ReactNode } from "react";
7+
8+
import styles from "./styles.module.css";
9+
10+
const LogoImage = ({ logo }: Readonly<Props>): React.JSX.Element => {
11+
const { withBaseUrl } = useBaseUrlUtils();
12+
const sources = {
13+
light: withBaseUrl(logo.src),
14+
dark: withBaseUrl(logo.srcDark ?? logo.src),
15+
};
16+
return (
17+
<ThemedImage
18+
className={clsx("footer__logo", logo.className)}
19+
alt={logo.alt}
20+
sources={sources}
21+
width={logo.width}
22+
height={logo.height}
23+
style={logo.style}
24+
/>
25+
);
26+
};
27+
28+
export default function FooterLogo({ logo }: Props): ReactNode {
29+
return logo.href ? (
30+
<Link href={logo.href} className={styles.footerLogoLink} target={logo.target}>
31+
<LogoImage logo={logo} />
32+
</Link>
33+
) : (
34+
<LogoImage logo={logo} />
35+
);
36+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.footerLogoLink {
2+
opacity: 0.5;
3+
transition: opacity var(--ifm-transition-fast)
4+
var(--ifm-transition-timing-default);
5+
}
6+
7+
.footerLogoLink:hover {
8+
opacity: 1;
9+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { useThemeConfig } from "@docusaurus/theme-common";
2+
import FooterCopyright from "@theme/Footer/Copyright";
3+
import FooterLayout from "@theme/Footer/Layout";
4+
import FooterLinks from "@theme/Footer/Links";
5+
import FooterLogo from "@theme/Footer/Logo";
6+
import React, { type ReactNode } from "react";
7+
8+
function Footer(): ReactNode {
9+
const { footer } = useThemeConfig();
10+
if (!footer) {
11+
return null;
12+
}
13+
const { copyright, links, logo, style } = footer;
14+
15+
return (
16+
<FooterLayout
17+
style={style}
18+
links={links && links.length > 0 && <FooterLinks links={links} />}
19+
logo={logo && <FooterLogo logo={logo} />}
20+
copyright={copyright && <FooterCopyright copyright={copyright} />}
21+
/>
22+
);
23+
}
24+
25+
export default React.memo(Footer);

0 commit comments

Comments
 (0)