Skip to content

Commit

Permalink
task: add hero icon and catalogue sections to home
Browse files Browse the repository at this point in the history
  • Loading branch information
NOMADE55 committed Dec 5, 2024
1 parent f15751d commit e5bcb6b
Show file tree
Hide file tree
Showing 6 changed files with 353 additions and 11 deletions.
202 changes: 202 additions & 0 deletions components/Catalogue.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import { FC, PropsWithChildren } from 'hono/jsx';
import { iconNames } from '../icons/index.ts';
import { kebabToName } from '../utils/icons.ts';
import { dirname, fromFileUrl, join } from '@std/path';
import { encodeBase64 } from '@std/encoding';
import Counter from './Counter.tsx';
import { containerClass } from './styles.ts';
import { css } from 'hono/css';

const CURRENT_DIR = dirname(fromFileUrl(import.meta.url));
const ICON_CATALOGUE = iconNames.reduce((acc, icon) => {
const f = icon[0];
if (!acc?.[f]) {
acc[f] = [];
}
acc[f].push(icon);
return acc;
}, {} as Record<string, string[]>);

const catalogueSort = (a: string, b: string) => {
const aNumber = Number.parseInt(a);
const bNumber = Number.parseInt(b);
if (Number.isNaN(aNumber) && Number.isNaN(bNumber)) return 0;
if (Number.isNaN(bNumber)) return +1;
if (Number.isNaN(aNumber)) return -1;
return aNumber < bNumber ? -1 : 1;
};

interface IconProps {
icon: string;
}

interface HeaderProps {
letter: string;
}

interface EntryProps {
letter: string;
icons: string[];
}

const catalogueEntryIcon = css`
display: flex;
gap: 2rem;
align-items: center;
cursor: pointer;
& + & {
margin-top: 2rem;
}
h4 {
font-size: 1.5rem;
}
&:hover {
img {
transform: scale(1.15);
filter: brightness(1.1);
}
h4 {
text-decoration: underline;
text-underline-offset: 4px;
text-decoration-thickness: 2px;
}
}
`;

const catalogueEntryIconImage = css`
width: 90px;
height: 90px;
border-radius: var(--rounded);
overflow: hidden;
img {
transition: all 150ms ease-in;
width: 100%; height: 100%; object-fit: cover;
}
`;

const Icon = async ({ icon }: PropsWithChildren<IconProps>) => {
return (
<div class={catalogueEntryIcon}>
<div class={catalogueEntryIconImage}>
<img
src={'data:image/svg+xml;base64,' + encodeBase64(
await Deno.readFile(join(CURRENT_DIR, `../icons/${icon}.svg`)),
)}
/>
</div>
<h4>{kebabToName(icon)}</h4>
</div>
);
};

const headerClass = css`
padding-left: .75rem;
padding-bottom: .25rem;
margin-bottom: 1.5rem;
position: sticky;
top: 0;
background-color: var(--dark-blue);
border-bottom: 2px solid rgba(255, 255, 255, 0.35);
cursor: pointer;
&:hover {
span {
display: block;
}
}
a {
display: flex;
align-items: center;
width: 100%;
text-decoration: none;
position: relative;
}
span {
font-size: 2rem;
left: -2rem;
position: absolute;
display: none;
}
h3 {
font-size: 2.5rem;
margin-bottom: 0;
}
`;

const Header = ({ letter }: PropsWithChildren<HeaderProps>) => (
<header class={headerClass} id={`${letter}-icons`}>
<a href={`/#${letter}-icons`}>
<span class='text-color__yellow'>#</span>
<h3>{letter.toUpperCase()}</h3>
</a>
</header>
);

const cataloguEntryClass = (iconCount: number = 1) =>
css`
position: relative;
grid-row: span ${iconCount + 1};
`;

const Entry = ({ letter, icons }: PropsWithChildren<EntryProps>) => (
<section class={cataloguEntryClass(icons.length)}>
<Header letter={letter} />
{icons.map((i) => <Icon icon={i} />)}
</section>
);

const catalogueIntroductionClass = css`
display: grid;
width: 100%;
grid-template-columns: 1fr 1fr;
gap: 2rem;
margin-bottom: 4rem;
`;

const catalogueMessageClass = css`
p {font-size: 1.3rem; line-height: 1.5;}
`;

const catalogueEntriesGridClass = css`
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
grid-auto-rows: 80px;
`;

const Catalogue: FC = () => {
return (
<>
<div class={containerClass}>
<div class={catalogueIntroductionClass}>
<div class={catalogueMessageClass}>
<h2>Current Catalogue</h2>
<p>
We're always adding new icons to our collection. However we might
be missing that one icon you need. Feel free to submit a PR to
{' '}
<a
class='text-color__yellow'
href='https://github.com/NOMADE55/picto'
>
the repository
</a>{' '}
or open an issue to request it!
</p>
</div>
<div class='catalogue-introduction--counter'>
<Counter />
</div>
</div>
<div className={catalogueEntriesGridClass}>
{Object.keys(ICON_CATALOGUE).sort(catalogueSort)
.map((letter) => (
<Entry letter={letter} icons={ICON_CATALOGUE[letter]} />
))}
</div>
</div>
</>
);
};

export default Catalogue;
87 changes: 87 additions & 0 deletions components/Hero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { FC } from 'hono/jsx';
import { containerClass } from './styles.ts';
import { css, cx } from 'hono/css';

const logoClass = css`
padding-top: 12rem;
margin-bottom: 3rem;
`;

const headlineClass = css`
font-size: 3rem;
font-weight: bold;
text-align: center;
`;

const headerClass = css`
margin-bottom: 4rem;
`;

const Hero: FC = () => (
<>
<header class={cx(containerClass, headerClass)}>
<h1 class={logoClass}>
<svg
version='1.1'
xmlns='http://www.w3.org/2000/svg'
xmlns:xlink='http://www.w3.org/1999/xlink'
x='0px'
y='0px'
viewBox='0 0 724 188'
xml:space='preserve'
overflow='visible'
>
<g>
<path
fill='transparent'
stroke-width='3'
stroke-dashoffset='-3'
stroke='#fff'
d='M210.4,94.4c0-53.2,43-94.1,99-94.1c23.2,0.3,48.1,10.5,66.5,27.2l-27.4,35.2c-11-10-25.6-16.6-38.9-16.6
c-27.7,0-50.7,21.1-50.7,48.6c0,27.9,24.1,48.4,49.7,48.4c15.1,0,28.5-7.1,40.1-17.5l27.2,36.2c-17.2,15.6-40,26.4-67.3,26.4
C257.8,188.3,210.4,150.8,210.4,94.4z'
/>
<polygon
fill='transparent'
stroke-width='3'
stroke-dashoffset='-3'
stroke='#fff'
points='525.1,45.7 481.2,45.7 481.2,188.3 432.5,188.3 432.5,45.7 388.6,45.7 388.6,0.3 525.1,0.3 '
/>
<path
fill='transparent'
stroke-width='3'
stroke-dashoffset='-3'
stroke='#fff'
d='M528.2,94.6c0-53.7,42.6-94.3,98.1-94.3c55.5,0,98.1,40.7,98.1,94.3c0,53.2-42.6,93.7-98.2,93.7
C570.7,188.3,528.2,147.8,528.2,94.6z M676.4,94.3c0-27.6-21.7-48.9-50.1-48.9c-28.4,0-50.1,21.3-50.1,48.9
c0,27.5,21.7,48.7,50.1,48.7C654.7,142.9,676.4,121.8,676.4,94.3z'
/>
<path
fill='transparent'
stroke-width='3'
stroke-dashoffset='-3'
stroke='#fff'
d='M186.5,188.3h-48.6V0.3h48.6V188.3z'
/>
<path
fill='var(--yellow)'
stroke-width='3'
stroke-dashoffset='-3'
stroke='var(--yellow)'
d='M26.9,139.6h48.7v48.7H26.9V139.6z M27.1,109c0-18.8,11.6-30.9,34.1-44.4c4.4-2.7,9-7.1,9-12.4
c0-8.7-6.7-14-19.1-14c-14.8,0-27.4,8-32.7,18L0.4,23.5C3.5,19.2,13.8,0.3,54.2,0.3c38.4,0,62.8,24.8,62.8,43.9
c0,16.4-3.4,25.4-17.1,36.1c-11.2,8.9-24.9,13.4-24.9,27.4c0,5.8,0.1,7.3,0.6,8.9l-48.3,0.1C27.1,115.5,27.1,114.2,27.1,109z'
/>
</g>
</svg>
<div class='sr-only'>Picto</div>
</h1>
<p class={headlineClass}>
Showcase your skills with flare 🔥
</p>
</header>
</>
);

export default Hero;
54 changes: 54 additions & 0 deletions components/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { css } from 'hono/css';

export const pageClass = css`
:-hono-global {
:root {
--yellow: #f3c922;
--dark-blue: #1a2b35;
--rounded: 12px;
font-size: 14px;
line-height: 1.2;
font-family: 'Fira Mono';
}
body {
background-color: var(--dark-blue);
color: #fff;
margin: 0;
}
* {
box-sizing: border-box;
&::selection {
background-color: var(--yellow);
}
}
p, input, button, blockquote {
margin: 0;
}
h1, h2, h3, h4, h5, h6 {
margin: 0;
font-family: Rubik;
margin-bottom: 1rem;
}
a {
text-decoration-thickness: 2px;
text-underline-offset: 4px;
color: inherit;
}
.sr-only {
display: none;
}
.text-color__yellow {
color: var(--yellow);
}
.text-color__blue {
color: var(--dark-blue);
}
}
`;

export const containerClass = css`
max-width: 1000px;
margin: auto;
padding: 1rem;
`;
15 changes: 5 additions & 10 deletions pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import type { FC } from 'hono/jsx';
import { css } from 'hono/css';

const pageClass = css`
:-hono-global {
:root { --yellow: #f3c922; --dark-blue: #1a2b35; font-size: 14px; line-height: 1.2; font-family: 'Fira Mono'; }
body { background-color: var(--dark-blue); color: #fff; margin: 0; }
* { box-sizing: border-box; }
h1, h2, h3, h4, h5, h6 { margin: 0; font-family: Rubik; margin-bottom: 1rem;}
}
`;
import Catalogue from '../components/Catalogue.tsx';
import Hero from '../components/Hero.tsx';
import { pageClass } from '../components/styles.ts';

const Home: FC = () => {
return (
<main class={pageClass}>
<Hero />
<Catalogue />
</main>
);
};
Expand Down
2 changes: 1 addition & 1 deletion pages/IconsRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const IconsRenderer = memo(({ icons, config }: Props) => {
>
{icons}
<g
className={`
class={`
theme-${theme}
${rounded === 200 ? 'rounded' : ''}
`}
Expand Down
4 changes: 4 additions & 0 deletions utils/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ export const parseIconParameters = async (
return acc;
}, [] as string[]);
};

export const kebabToName = (kebab: string) =>
kebab.split('-').map((w: string) => w.charAt(0).toUpperCase() + w.slice(1))
.join(' ');

0 comments on commit e5bcb6b

Please sign in to comment.