Implement redesign of portfolio example (#5765)
Co-authored-by: Yan Thomas <61414485+Yan-Thomas@users.noreply.github.com> Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
|
@ -9,7 +9,7 @@ npm create astro@latest -- --template portfolio
|
|||
|
||||
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||
|
||||
![portfolio](https://user-images.githubusercontent.com/4677417/186189473-03dda103-65d3-4220-8b60-180ccaee5939.png)
|
||||
![portfolio](https://user-images.githubusercontent.com/357379/210779178-a98f0fb7-6b1a-4068-894c-8e1403e26654.jpg)
|
||||
|
||||
|
||||
## 🧞 Commands
|
||||
|
|
BIN
examples/portfolio/public/assets/at-work.jpg
Normal file
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1 @@
|
|||
<svg height="640" width="1440" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a"><stop offset=".58" stop-opacity="0"/><stop offset="1"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="793.5" x2="759.5" xlink:href="#a" y1="261.5" y2="149.5"/><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="644.19" x2="645.54" xlink:href="#a" y1="398.02" y2="267.7"/><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="547" x2="522.36" xlink:href="#a" y1="457.27" y2="342.85"/><g clip-rule="evenodd" fill-rule="evenodd" opacity=".15"><path d="m439.57 249.55a2149.47 2149.47 0 0 1 1193.87-182.45l-12.48 93.17a2055.46 2055.46 0 0 0 -1141.66 174.47l-454.24 211.86-39.73-85.2z" fill="url(#b)"/><path d="m272.3 266.93a2393.36 2393.36 0 0 1 1328.96 205.6l-44.42 94.78a2288.7 2288.7 0 0 0 -1270.84-196.61l-553.29 73.05-13.7-103.77z" fill="url(#c)" opacity=".56"/><path d="m195.26 416.13a2149.46 2149.46 0 0 1 1204.86-83.21l-20.13 91.82a2055.46 2055.46 0 0 0 -1152.17 79.56l-470.18 173.62-32.56-88.18 470.18-173.62z" fill="url(#d)"/></g><path d="m-258.15 719.56 1743.12-517.56 182.93 616.12-1743.1 517.56z" fill="#090b11"/></svg>
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1440" height="640"><g opacity=".15"><path fill="url(#a)" d="M439.57 249.55A2149.47 2149.47 0 0 1 1633.44 67.1l-12.48 93.17A2055.46 2055.46 0 0 0 479.3 334.74L25.06 546.6l-39.73-85.2z"/><path fill="url(#b)" d="M272.3 265.93a2393.36 2393.36 0 0 1 1328.96 205.6l-44.42 94.78A2288.7 2288.7 0 0 0 286 369.7l-553.29 73.05-13.7-103.77z" opacity=".56"/><path fill="url(#c)" d="M195.26 416.13a2149.47 2149.47 0 0 1 1204.86-83.21l-20.13 91.82A2055.46 2055.46 0 0 0 227.82 504.3l-470.18 173.62-32.56-88.18 470.18-173.62z"/></g><path fill="#fff" d="M-258 718.56 1485.12 201l182.93 616.12-1743.11 517.56z"/><defs><linearGradient id="d"><stop offset=".58" stop-opacity="0"/><stop offset="1"/></linearGradient><linearGradient xlink:href="#d" id="a" x1="793.5" x2="759.5" y1="261.5" y2="149.5" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#d" id="b" x1="644.19" x2="645.54" y1="397.02" y2="266.7" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#d" id="c" x1="547" x2="522.36" y1="457.27" y2="342.85" gradientUnits="userSpaceOnUse"/></defs></svg>
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 5.3 KiB |
BIN
examples/portfolio/public/assets/backgrounds/noise.png
Normal file
After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 21 KiB |
BIN
examples/portfolio/public/assets/portrait.jpg
Normal file
After Width: | Height: | Size: 129 KiB |
BIN
examples/portfolio/public/assets/stock-1.jpg
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
examples/portfolio/public/assets/stock-2.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
examples/portfolio/public/assets/stock-3.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
examples/portfolio/public/assets/stock-4.jpg
Normal file
After Width: | Height: | Size: 27 KiB |
56
examples/portfolio/src/components/CallToAction.astro
Normal file
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
interface Props {
|
||||
href: string;
|
||||
}
|
||||
|
||||
const { href } = Astro.props;
|
||||
---
|
||||
|
||||
<a href={href}><slot /></a>
|
||||
|
||||
<style>
|
||||
a {
|
||||
position: relative;
|
||||
display: flex;
|
||||
place-content: center;
|
||||
text-align: center;
|
||||
padding: 0.56em 2em;
|
||||
gap: 0.8em;
|
||||
color: var(--accent-text-over);
|
||||
text-decoration: none;
|
||||
line-height: 1.1;
|
||||
border-radius: 999rem;
|
||||
overflow: hidden;
|
||||
background: var(--gradient-accent-orange);
|
||||
box-shadow: var(--shadow-md);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (min-width: 20em) {
|
||||
a {
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Overlay for hover effects. */
|
||||
a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
transition: background-color var(--theme-transition);
|
||||
mix-blend-mode: overlay;
|
||||
}
|
||||
|
||||
a:focus::after,
|
||||
a:hover::after {
|
||||
background-color: hsla(var(--gray-999-basis), 0.3);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
a {
|
||||
padding: 1.125rem 2.5rem;
|
||||
font-size: var(--text-xl);
|
||||
}
|
||||
}
|
||||
</style>
|
46
examples/portfolio/src/components/ContactCTA.astro
Normal file
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
import CallToAction from './CallToAction.astro';
|
||||
import Icon from './Icon.astro';
|
||||
---
|
||||
|
||||
<aside>
|
||||
<h2>Interested in working together?</h2>
|
||||
<CallToAction href="mailto:me@example.com">
|
||||
Send Me a Message
|
||||
<Icon icon="paper-plane-tilt" size="1.2em" />
|
||||
</CallToAction>
|
||||
</aside>
|
||||
|
||||
<style>
|
||||
aside {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 3rem;
|
||||
border-top: 1px solid var(--gray-800);
|
||||
border-bottom: 1px solid var(--gray-800);
|
||||
padding: 5rem 1.5rem;
|
||||
background-color: var(--gray-999_40);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--text-xl);
|
||||
text-align: center;
|
||||
max-width: 15ch;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
aside {
|
||||
padding: 7.5rem;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--text-3xl);
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,25 +1,74 @@
|
|||
---
|
||||
import Icon from './Icon.astro';
|
||||
const currentYear = new Date().getFullYear();
|
||||
---
|
||||
|
||||
<footer>
|
||||
© {currentYear} Jeanine White
|
||||
<small class="byline">🚀 Built by Astro</small>
|
||||
<div class="group">
|
||||
<p>
|
||||
Designed & Developed in Portland with <a href="https://astro.build/">Astro</a>
|
||||
<Icon icon="rocket-launch" size="1.2em" />
|
||||
</p>
|
||||
<p>© {currentYear} Jeanine White</p>
|
||||
</div>
|
||||
<p class="socials">
|
||||
<a href="https://twitter.com/me"> Twitter</a>
|
||||
<a href="https://github.com/me"> GitHub</a>
|
||||
<a href="https://codepen.io/me"> CodePen</a>
|
||||
</p>
|
||||
</footer>
|
||||
<style>
|
||||
footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3rem;
|
||||
margin-top: auto;
|
||||
padding: 3rem 2rem 3rem;
|
||||
text-align: center;
|
||||
padding-top: 8rem;
|
||||
padding-right: 2rem;
|
||||
padding-bottom: 4rem;
|
||||
padding-left: 2rem;
|
||||
color: var(--gray-400);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
|
||||
.byline {
|
||||
display: block;
|
||||
margin-top: 1rem;
|
||||
color: var(--t-subdue);
|
||||
font-size: var(--f-d2);
|
||||
text-transform: uppercase;
|
||||
footer a {
|
||||
color: var(--gray-400);
|
||||
text-decoration: 1px solid underline transparent;
|
||||
text-underline-offset: 0.25em;
|
||||
transition: text-decoration-color var(--theme-transition);
|
||||
}
|
||||
|
||||
footer a:hover,
|
||||
footer a:focus {
|
||||
text-decoration-color: currentColor;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.socials {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
footer {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 2.5rem 5rem;
|
||||
}
|
||||
|
||||
.group {
|
||||
flex-direction: row;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.socials {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
65
examples/portfolio/src/components/Grid.astro
Normal file
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
interface Props {
|
||||
variant?: 'offset' | 'small';
|
||||
}
|
||||
|
||||
const { variant } = Astro.props;
|
||||
---
|
||||
|
||||
<ul class:list={['grid', { offset: variant === 'offset', small: variant === 'small' }]}>
|
||||
<slot />
|
||||
</ul>
|
||||
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-auto-rows: 1fr;
|
||||
gap: 1rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.grid.small {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
/* If last row contains only one item, make it span both columns. */
|
||||
.grid.small > :global(:last-child:nth-child(odd)) {
|
||||
grid-column: 1 / 3;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
.grid.offset {
|
||||
--row-offset: 7.5rem;
|
||||
padding-bottom: var(--row-offset);
|
||||
}
|
||||
|
||||
/* Shift first item in each row vertically to create staggered effect. */
|
||||
.grid.offset > :global(:nth-child(odd)) {
|
||||
transform: translateY(var(--row-offset));
|
||||
}
|
||||
|
||||
/* If last row contains only one item, display it in the second column. */
|
||||
.grid.offset > :global(:last-child:nth-child(odd)) {
|
||||
grid-column: 2 / 3;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.grid.small {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.grid.small > :global(*) {
|
||||
flex-basis: 20rem;
|
||||
}
|
||||
}
|
||||
</style>
|
54
examples/portfolio/src/components/Hero.astro
Normal file
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
interface Props {
|
||||
title: string;
|
||||
tagline?: string;
|
||||
align?: 'start' | 'center';
|
||||
}
|
||||
|
||||
const { align = 'center', tagline, title } = Astro.props;
|
||||
---
|
||||
|
||||
<header class:list={['hero stack gap-4', align]}>
|
||||
<div class="stack gap-2">
|
||||
<h1 class="title">{title}</h1>
|
||||
{tagline && <p class="tagline">{tagline}</p>}
|
||||
</div>
|
||||
<slot />
|
||||
</header>
|
||||
|
||||
<style>
|
||||
.hero {
|
||||
font-size: var(--text-lg);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title,
|
||||
.tagline {
|
||||
max-width: 37ch;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: var(--text-3xl);
|
||||
color: var(--gray-0);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.hero {
|
||||
font-size: var(--text-xl);
|
||||
}
|
||||
|
||||
.start {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.start .title,
|
||||
.start .tagline {
|
||||
margin-inline: unset;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: var(--text-5xl);
|
||||
}
|
||||
}
|
||||
</style>
|
55
examples/portfolio/src/components/Icon.astro
Normal file
|
@ -0,0 +1,55 @@
|
|||
---
|
||||
import type { HTMLAttributes } from 'astro/types';
|
||||
import { iconPaths } from './IconPaths';
|
||||
|
||||
export interface Props {
|
||||
icon: keyof typeof iconPaths;
|
||||
color?: string;
|
||||
gradient?: boolean;
|
||||
size?: string;
|
||||
}
|
||||
|
||||
const { color = 'currentcolor', gradient, icon, size } = Astro.props;
|
||||
const iconPath = iconPaths[icon];
|
||||
|
||||
const attrs: HTMLAttributes<'svg'> = {};
|
||||
if (size) attrs.style = { '--size': size };
|
||||
|
||||
const gradientId = 'icon-gradient-' + Math.round(Math.random() * 10e12).toString(36);
|
||||
---
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 256 256"
|
||||
aria-hidden="true"
|
||||
stroke={gradient ? `url(#${gradientId})` : color}
|
||||
{...attrs}
|
||||
>
|
||||
<g set:html={iconPath} />
|
||||
{
|
||||
gradient && (
|
||||
<linearGradient
|
||||
id={gradientId}
|
||||
x1="23"
|
||||
x2="235"
|
||||
y1="43"
|
||||
y2="202"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--gradient-stop-1)" />
|
||||
<stop offset=".5" stop-color="var(--gradient-stop-2)" />
|
||||
<stop offset="1" stop-color="var(--gradient-stop-3)" />
|
||||
</linearGradient>
|
||||
)
|
||||
}
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
width: var(--size, 1em);
|
||||
height: var(--size, 1em);
|
||||
}
|
||||
</style>
|
23
examples/portfolio/src/components/IconPaths.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
/** Icons adapted from https://phosphoricons.com/ */
|
||||
export const iconPaths = {
|
||||
'terminal-window': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m80 96 40 32-40 32m56 0h40"/><rect width="192" height="160" x="32" y="48" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16.97" rx="8.5"/>`,
|
||||
trophy: `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M56 56v55.1c0 39.7 31.8 72.6 71.5 72.9a72 72 0 0 0 72.5-72V56a8 8 0 0 0-8-8H64a8 8 0 0 0-8 8Zm40 168h64m-32-40v40"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M198.2 128h9.8a32 32 0 0 0 32-32V80a8 8 0 0 0-8-8h-32M58 128H47.9a32 32 0 0 1-32-32V80a8 8 0 0 1 8-8h32"/>`,
|
||||
strategy: `<circle cx="68" cy="188" r="28" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m40 72 40 40m0-40-40 40m136 56 40 40m0-40-40 40M136 80V40h40"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m136 40 16 16c40 40 8 88-24 96"/>`,
|
||||
'paper-plane-tilt': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M210.3 35.9 23.9 88.4a8 8 0 0 0-1.2 15l85.6 40.5a7.8 7.8 0 0 1 3.8 3.8l40.5 85.6a8 8 0 0 0 15-1.2l52.5-186.4a7.9 7.9 0 0 0-9.8-9.8Zm-99.4 109.2 45.2-45.2"/>`,
|
||||
'arrow-right': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M40 128h176m-72-72 72 72-72 72"/>`,
|
||||
'arrow-left': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M216 128H40m72-72-72 72 72 72"/>`,
|
||||
code: `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m64 88-48 40 48 40m128-80 48 40-48 40M160 40 96 216"/>`,
|
||||
'microphone-stage': `<circle cx="168" cy="88" r="64" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m213.3 133.3-90.6-90.6M100 156l-12 12m16.8-70.1L28.1 202.5a7.9 7.9 0 0 0 .8 10.4l14.2 14.2a7.9 7.9 0 0 0 10.4.8l104.6-76.7"/>`,
|
||||
'pencil-line': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M96 216H48a8 8 0 0 1-8-8v-44.7a7.9 7.9 0 0 1 2.3-5.6l120-120a8 8 0 0 1 11.4 0l44.6 44.6a8 8 0 0 1 0 11.4Zm40-152 56 56"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M216 216H96l-55.5-55.5M164 92l-96 96"/>`,
|
||||
'rocket-launch': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M94.1 184.6c-11.4 33.9-56.6 33.9-56.6 33.9s0-45.2 33.9-56.6m124.5-56.5L128 173.3 82.7 128l67.9-67.9C176.3 34.4 202 34.7 213 36.3a7.8 7.8 0 0 1 6.7 6.7c1.6 11 1.9 36.7-23.8 62.4Z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M184.6 116.7v64.6a8 8 0 0 1-2.4 5.6l-32.3 32.4a8 8 0 0 1-13.5-4.1l-8.4-41.9m11.3-101.9H74.7a8 8 0 0 0-5.6 2.4l-32.4 32.3a8 8 0 0 0 4.1 13.5l41.9 8.4"/>`,
|
||||
list: `<path stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M40 128h176M40 64h176M40 192h176"/>`,
|
||||
heart: `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M128 216S28 160 28 92a52 52 0 0 1 100-20h0a52 52 0 0 1 100 20c0 68-100 124-100 124Z"/>`,
|
||||
'moon-stars': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M216 112V64m24 24h-48m-24-64v32m16-16h-32m65 113A92 92 0 0 1 103 39h0a92 92 0 1 0 114 114Z"/>`,
|
||||
sun: `<circle cx="128" cy="128" r="60" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M128 36V16M63 63 49 49m-13 79H16m47 65-14 14m79 13v20m65-47 14 14m13-79h20m-47-65 14-14"/>`,
|
||||
'twitter-logo': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M128 88c0-22 18.5-40.3 40.5-40a40 40 0 0 1 36.2 24H240l-32.3 32.3A127.9 127.9 0 0 1 80 224c-32 0-40-12-40-12s32-12 48-36c0 0-64-32-48-120 0 0 40 40 88 48Z"/>`,
|
||||
'codepen-logo': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m232 101-104 59-104-59 100.1-56.8a8.3 8.3 0 0 1 7.8 0Z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m232 165-100.1 56.8a8.3 8.3 0 0 1-7.8 0L24 165l104-59Zm0-64v64M24 101v64m104-5v62.8m0-179.6V106"/>`,
|
||||
'github-logo': `<g stroke-linecap="round" stroke-linejoin="round"><path fill="none" stroke-width="14.7" d="M55.7 167.2c13.9 1 21.3 13.1 22.2 14.6 4.2 7.2 10.4 9.6 18.3 7.1l1.1-3.4a60.3 60.3 0 0 1-25.8-11.9c-12-10.1-18-25.6-18-46.3"/><path fill="none" stroke-width="16" d="M61.4 205.1a24.5 24.5 0 0 1-3-6.1c-3.2-7.9-7.1-10.6-7.8-11.1l-1-.6c-2.4-1.6-9.5-6.5-7.2-13.9 1.4-4.5 6-7.2 12.3-7.2h.8c4 .3 7.6 1.5 10.7 3.2-9.1-10.1-13.6-24.3-13.6-42.3 0-11.3 3.5-21.7 10.1-30.4A46.7 46.7 0 0 1 65 67.3a8.3 8.3 0 0 1 5-4.7c2.8-.9 13.3-2.7 33.2 9.9a105 105 0 0 1 50.5 0c19.9-12.6 30.4-10.8 33.2-9.9 2.3.7 4.1 2.4 5 4.7 5 12.7 4 23.2 2.6 29.4 6.7 8.7 10 18.9 10 30.4 0 42.6-25.8 54.7-43.6 58.7 1.4 4.1 2.2 8.8 2.2 13.7l-.1 23.4v2.3"/><path fill="none" stroke-width="16" d="M160.9 185.7c1.4 4.1 2.2 8.8 2.2 13.7l-.1 23.4v2.3A98.6 98.6 0 1 0 61.4 205c-1.4-2.1-11.3-17.5-11.8-17.8-2.4-1.6-9.5-6.5-7.2-13.9 1.4-4.5 6-7.2 12.3-7.2h.8c4 .3 7.6 1.5 10.7 3.2-9.1-10.1-13.6-24.3-13.6-42.3 0-11.3 3.5-21.7 10.1-30.4A46.4 46.4 0 0 1 65 67.3a8.3 8.3 0 0 1 5-4.7c2.8-.9 13.3-2.7 33.2 9.9a105 105 0 0 1 50.5 0c19.9-12.6 30.4-10.8 33.2-9.9 2.3.7 4.1 2.4 5 4.7 5 12.7 4 23.2 2.6 29.4 6.7 8.7 10 18.9 10 30.4.1 42.6-25.8 54.7-43.6 58.6z"/><path fill="none" stroke-width="18.7" d="m170.1 203.3 17.3-12 17.2-18.7 9.5-26.6v-27.9l-9.5-27.5" /><path fill="none" stroke-width="22.7" d="m92.1 57.3 23.3-4.6 18.7-1.4 29.3 5.4m-110 32.6-8 16-4 21.4.6 20.3 3.4 13" /><path fill="none" stroke-width="13.3" d="M28.8 133a100 100 0 0 0 66.9 94.4v-8.7c-22.4 1.8-33-11.5-35.6-19.8-3.4-8.6-7.8-11.4-8.5-11.8"/></g>`,
|
||||
'twitch-logo': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M165 200h-42a8 8 0 0 0-5 2l-46 38v-40H48a8 8 0 0 1-8-8V48a8 8 0 0 1 8-8h160a8 8 0 0 1 8 8v108a8 8 0 0 1-3 6l-43 36a8 8 0 0 1-5 2Zm3-112v48m-48-48v48"/>`,
|
||||
'youtube-logo': `<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="m160 128-48-32v64l48-32z"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M24 128c0 30 3 47 5 56a16 16 0 0 0 10 11c34 13 89 13 89 13s56 0 89-13a16 16 0 0 0 10-11c2-9 5-26 5-56s-3-47-5-56a16 16 0 0 0-10-11c-33-13-89-13-89-13s-55 0-89 13a16 16 0 0 0-10 11c-2 9-5 26-5 56Z"/>`,
|
||||
'dribbble-logo': `<circle cx="128" cy="128" r="96" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M71 205a160 160 0 0 1 137-77l16 1m-36-76a160 160 0 0 1-124 59 165 165 0 0 1-30-3"/><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" d="M86 42a161 161 0 0 1 74 177"/>`,
|
||||
};
|
|
@ -1,5 +1,11 @@
|
|||
---
|
||||
import '../styles/global.css';
|
||||
|
||||
interface Props {
|
||||
title?: string | undefined;
|
||||
description?: string | undefined;
|
||||
}
|
||||
|
||||
const {
|
||||
title = 'Jeanine White: Personal Site',
|
||||
description = 'The personal site of Jeanine White',
|
||||
|
@ -16,6 +22,27 @@ const {
|
|||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@200;400;700;900&display=swap"
|
||||
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,400;0,700;1,400&family=Rubik:wght@500;600&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<script is:inline>
|
||||
// This code is inlined in the head to make dark mode instant & blocking.
|
||||
const getThemePreference = () => {
|
||||
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
|
||||
return localStorage.getItem('theme');
|
||||
}
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
};
|
||||
const isDark = getThemePreference() === 'dark';
|
||||
document.documentElement.classList[isDark ? 'add' : 'remove']('theme-dark');
|
||||
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
// Watch the document element and persist user preference when it changes.
|
||||
const observer = new MutationObserver(() => {
|
||||
const isDark = document.documentElement.classList.contains('theme-dark');
|
||||
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
||||
});
|
||||
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,99 +1,324 @@
|
|||
---
|
||||
import Icon, { Props as IconProps } from './Icon.astro';
|
||||
import ThemeToggle from './ThemeToggle.astro';
|
||||
|
||||
/** Main menu items */
|
||||
const textLinks: { label: string; href: string }[] = [
|
||||
{ label: 'Home', href: '/' },
|
||||
{ label: 'Work', href: '/work/' },
|
||||
{ label: 'About', href: '/about/' },
|
||||
];
|
||||
|
||||
/** Icon links to social media — edit these with links to your profiles! */
|
||||
const iconLinks: { label: string; href: string; icon: IconProps['icon'] }[] = [
|
||||
{ label: 'Twitter', href: 'https://twitter.com/me', icon: 'twitter-logo' },
|
||||
{ label: 'Twitch', href: 'https://twitch.tv/me', icon: 'twitch-logo' },
|
||||
{ label: 'GitHub', href: 'https://github.com/me', icon: 'github-logo' },
|
||||
{ label: 'CodePen', href: 'https://codepen.io/me', icon: 'codepen-logo' },
|
||||
{ label: 'dribbble', href: 'https://dribbble.com/me', icon: 'dribbble-logo' },
|
||||
{ label: 'YouTube', href: 'https://www.youtube.com/@me/', icon: 'youtube-logo' },
|
||||
];
|
||||
---
|
||||
|
||||
<nav>
|
||||
<a class="link" href="/">
|
||||
<div class="monogram">JW</div>
|
||||
</a>
|
||||
<a class="link" href="/projects/"> Portfolio</a>
|
||||
<a class="link" href="/about/"> About</a>
|
||||
<div class="socials">
|
||||
<a class="social" href="https://twitter.com/me">
|
||||
<svg class="socialIcon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"
|
||||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="social" href="https://github.com/me">
|
||||
<svg class="socialIcon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
||||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="social" href="https://dev.to/me">
|
||||
<svg
|
||||
class="socialIcon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 50 40"
|
||||
style="enable-background:new 0 0 50 40"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<path
|
||||
d="M15.7 15.5c-.4-.3-.7-.4-1.1-.4h-1.7v10.1h1.7c.4 0 .8-.1 1.1-.4.4-.3.6-.7.6-1.3v-6.7c0-.6-.2-1-.6-1.3z"
|
||||
></path>
|
||||
<path
|
||||
d="M47 0H3C1.3 0 0 1.3 0 3v34c0 1.7 1.3 3 3 3h44c1.7 0 3-1.3 3-3V3c0-1.7-1.3-3-3-3zM19.1 23.5c0 1.3-.4 2.4-1.3 3.2-.8.9-1.9 1.3-3.3 1.3h-4.4V12.3h4.5c1.3 0 2.4.4 3.2 1.3.8.8 1.3 1.9 1.3 3.2v6.7zm9.1-8.4h-5.1v3.6h3.1v2.8h-3.1v3.7h5.1V28h-5.9c-.6 0-1-.2-1.4-.6-.4-.4-.6-.8-.6-1.4V14.2c0-.6.2-1 .6-1.4.4-.4.8-.6 1.4-.6h5.9v2.9zM37.5 26c-.6 1.3-1.3 2-2.2 2-.9 0-1.7-.7-2.2-2l-3.7-13.8h3.1L35.3 23l2.8-10.8h3.1L37.5 26z"
|
||||
></path>
|
||||
</svg>
|
||||
<div class="menu-header">
|
||||
<a href="/" class="site-title">
|
||||
<Icon icon="terminal-window" color="var(--accent-regular)" size="1.6em" gradient />
|
||||
Jeanine White
|
||||
</a>
|
||||
<menu-button>
|
||||
<template>
|
||||
<button class="menu-button" aria-expanded="false">
|
||||
<span class="sr-only">Menu</span>
|
||||
<Icon icon="list" />
|
||||
</button>
|
||||
</template>
|
||||
</menu-button>
|
||||
</div>
|
||||
<div id="menu-content">
|
||||
<ul class="nav-items">
|
||||
{
|
||||
textLinks.map(({ label, href }) => (
|
||||
<li>
|
||||
<a
|
||||
aria-current={Astro.url.pathname === href}
|
||||
class:list={[
|
||||
'link',
|
||||
{
|
||||
active:
|
||||
Astro.url.pathname === href ||
|
||||
(href !== '/' && Astro.url.pathname.startsWith(href)),
|
||||
},
|
||||
]}
|
||||
href={href}
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
<div class="menu-footer">
|
||||
<div class="socials">
|
||||
{
|
||||
iconLinks.map(({ href, icon, label }) => (
|
||||
<a href={href} class="social">
|
||||
<span class="sr-only">{label}</span>
|
||||
<Icon icon={icon} />
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="theme-toggle">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<script>
|
||||
class MenuButton extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// Inject menu toggle button when JS runs.
|
||||
this.appendChild(this.querySelector('template')!.content.cloneNode(true));
|
||||
const btn = this.querySelector('button')!;
|
||||
|
||||
// Hide menu (shown by default to support no-JS browsers).
|
||||
const menu = document.getElementById('menu-content')!;
|
||||
menu.hidden = true;
|
||||
|
||||
/** Set whether the menu is currently expanded or collapsed. */
|
||||
const setExpanded = (expand: boolean) => {
|
||||
btn.setAttribute('aria-expanded', expand ? 'true' : 'false');
|
||||
menu.hidden = !expand;
|
||||
};
|
||||
|
||||
// Toggle menu visibility when the menu button is clicked.
|
||||
btn.addEventListener('click', () => setExpanded(menu.hidden));
|
||||
|
||||
// Hide menu button for large screens.
|
||||
const handleViewports = (e: MediaQueryList | MediaQueryListEvent) => {
|
||||
setExpanded(e.matches);
|
||||
btn.hidden = e.matches;
|
||||
};
|
||||
const mediaQueries = window.matchMedia('(min-width: 50em)');
|
||||
handleViewports(mediaQueries);
|
||||
mediaQueries.addEventListener('change', handleViewports);
|
||||
}
|
||||
}
|
||||
customElements.define('menu-button', MenuButton);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
nav {
|
||||
z-index: 9999;
|
||||
position: relative;
|
||||
font-family: var(--font-brand);
|
||||
font-weight: 500;
|
||||
margin-bottom: 3.5rem;
|
||||
}
|
||||
|
||||
.menu-header {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.site-title {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
line-height: 1.1;
|
||||
color: var(--gray-0);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.menu-button {
|
||||
position: relative;
|
||||
display: flex;
|
||||
border: 0;
|
||||
border-radius: 999rem;
|
||||
padding: 0.5rem;
|
||||
font-size: 1.5rem;
|
||||
color: var(--gray-300);
|
||||
background: radial-gradient(var(--gray-900), var(--gray-800) 150%);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.menu-button[aria-expanded='true'] {
|
||||
color: var(--gray-0);
|
||||
background: linear-gradient(180deg, var(--gray-600), transparent),
|
||||
radial-gradient(var(--gray-900), var(--gray-800) 150%);
|
||||
}
|
||||
|
||||
.menu-button[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu-button::before {
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
content: '';
|
||||
background: var(--gradient-stroke);
|
||||
border-radius: 999rem;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
#menu-content {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.nav-items {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
font-size: var(--text-md);
|
||||
line-height: 1.2;
|
||||
list-style: none;
|
||||
padding: 2rem;
|
||||
background-color: var(--gray-999);
|
||||
border-bottom: 1px solid var(--gray-800);
|
||||
}
|
||||
|
||||
.link {
|
||||
color: var(--t-subdue);
|
||||
display: block;
|
||||
display: inline-block;
|
||||
color: var(--gray-300);
|
||||
text-decoration: none;
|
||||
font-size: var(--f-d1);
|
||||
text-transform: uppercase;
|
||||
padding-top: 0.75em;
|
||||
padding-bottom: 0.75em;
|
||||
}
|
||||
|
||||
.link:hover,
|
||||
.link:focus {
|
||||
color: var(--t-active);
|
||||
.link.active {
|
||||
color: var(--gray-0);
|
||||
}
|
||||
|
||||
.monogram {
|
||||
.menu-footer {
|
||||
--icon-size: var(--text-xl);
|
||||
--icon-padding: 0.5rem;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
margin-right: 0.5rem;
|
||||
color: var(--c-black);
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.125rem;
|
||||
border: 3px solid currentColor;
|
||||
border-radius: 50%;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
padding: 1.5rem 2rem 1.5rem 1.5rem;
|
||||
background-color: var(--gray-999);
|
||||
border-radius: 0 0 0.75rem 0.75rem;
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.socials {
|
||||
display: flex;
|
||||
gap: 0.75em;
|
||||
margin-left: auto;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.625rem;
|
||||
font-size: var(--icon-size);
|
||||
}
|
||||
|
||||
.social {
|
||||
display: block;
|
||||
display: flex;
|
||||
padding: var(--icon-padding);
|
||||
text-decoration: none;
|
||||
color: var(--accent-dark);
|
||||
transition: color var(--theme-transition);
|
||||
}
|
||||
|
||||
.socialIcon {
|
||||
display: block;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
fill: var(--t-subdue);
|
||||
transition: fill linear 150ms;
|
||||
.social:hover,
|
||||
.social:focus {
|
||||
color: var(--accent-text-over);
|
||||
}
|
||||
|
||||
.socialIcon:hover {
|
||||
fill: var(--t-active);
|
||||
.theme-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: calc(var(--icon-size) + 2 * var(--icon-padding));
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
nav {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
align-items: center;
|
||||
padding: 2.5rem 5rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.menu-header {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.site-title {
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
|
||||
#menu-content {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.nav-items {
|
||||
position: relative;
|
||||
flex-direction: row;
|
||||
font-size: var(--text-sm);
|
||||
border-radius: 999rem;
|
||||
border: 0;
|
||||
padding: 0.5rem 0.5625rem;
|
||||
background: radial-gradient(var(--gray-900), var(--gray-800) 150%);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.nav-items::before {
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
content: '';
|
||||
background: var(--gradient-stroke);
|
||||
border-radius: 999rem;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.link {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 999rem;
|
||||
transition: color var(--theme-transition), background-color var(--theme-transition);
|
||||
}
|
||||
|
||||
.link:hover,
|
||||
.link:focus {
|
||||
color: var(--gray-100);
|
||||
background-color: var(--accent-subtle-overlay);
|
||||
}
|
||||
|
||||
.link.active {
|
||||
color: var(--accent-text-over);
|
||||
background-color: var(--accent-regular);
|
||||
}
|
||||
|
||||
.menu-footer {
|
||||
--icon-padding: 0.375rem;
|
||||
|
||||
justify-self: flex-end;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.socials {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 60em) {
|
||||
.socials {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0;
|
||||
}
|
||||
}
|
||||
@media (forced-colors: active) {
|
||||
.link.active {
|
||||
color: SelectedItem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
16
examples/portfolio/src/components/Pill.astro
Normal file
|
@ -0,0 +1,16 @@
|
|||
<div class="pill"><slot /></div>
|
||||
|
||||
<style>
|
||||
.pill {
|
||||
display: flex;
|
||||
padding: 0.5rem 1rem;
|
||||
gap: 0.5rem;
|
||||
color: var(--accent-text-over);
|
||||
border: 1px solid var(--accent-regular);
|
||||
background-color: var(--accent-regular);
|
||||
border-radius: 999rem;
|
||||
font-size: var(--text-md);
|
||||
line-height: 1.35;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
|
@ -1,144 +1,64 @@
|
|||
---
|
||||
import type { MarkdownInstance } from 'astro';
|
||||
import type { Project } from '../types';
|
||||
import type { CollectionEntry } from 'astro:content';
|
||||
|
||||
interface Props {
|
||||
project: MarkdownInstance<Project>;
|
||||
project: CollectionEntry<'work'>;
|
||||
}
|
||||
|
||||
const { frontmatter, url } = Astro.props.project;
|
||||
const { data, slug } = Astro.props.project;
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
<div class="titleCard" style={`background-image:url(${frontmatter.img})`}>
|
||||
<h1 class="title">{frontmatter.title}</h1>
|
||||
</div>
|
||||
<div class="descCard">
|
||||
<p class="desc">{frontmatter.description}</p>
|
||||
<div class="tags">
|
||||
Tagged:
|
||||
{
|
||||
frontmatter.tags.map((t) => (
|
||||
<div class="tag" data-tag={t}>
|
||||
{t}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<a class="link" href={url}>
|
||||
<span class="linkInner">View</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<a class="card" href={`/work/${slug}`}>
|
||||
<span class="title">{data.title}</span>
|
||||
<img src={data.img} alt={data.img_alt || ''} loading="lazy" decoding="async" />
|
||||
</a>
|
||||
|
||||
<style>
|
||||
.card {
|
||||
position: relative;
|
||||
color: var(--t-bg);
|
||||
background: var(--t-fg);
|
||||
border: 1px solid #f0f0f0;
|
||||
display: grid;
|
||||
grid-template: auto 1fr / auto 1fr;
|
||||
height: 11rem;
|
||||
background: var(--gradient-subtle);
|
||||
border: 1px solid var(--gray-800);
|
||||
border-radius: 0.75rem;
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-sm);
|
||||
text-decoration: none;
|
||||
font-family: var(--font-brand);
|
||||
font-size: var(--text-lg);
|
||||
font-weight: 500;
|
||||
transition: box-shadow var(--theme-transition);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.title {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
grid-area: 1 / 1 / 2 / 2;
|
||||
z-index: 1;
|
||||
margin: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--gray-999);
|
||||
color: var(--gray-200);
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
img {
|
||||
grid-area: 1 / 1 / 3 / 3;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
color: white;
|
||||
flex-direction: column;
|
||||
font-size: var(--f-u4);
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.0625em;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.titleCard {
|
||||
position: relative;
|
||||
background-size: cover;
|
||||
background-position: 50% 100%;
|
||||
padding-top: 37.5%;
|
||||
}
|
||||
@media (min-width: 50em) {
|
||||
.card {
|
||||
height: 22rem;
|
||||
border-radius: 1.5rem;
|
||||
}
|
||||
|
||||
.descCard {
|
||||
padding: 1.5em;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: var(--f-u1);
|
||||
line-height: 1.4;
|
||||
margin-top: 0em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.link {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--t-bg);
|
||||
font-size: var(--f-u2);
|
||||
font-weight: 700;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
opacity: 0;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
transition: opacity 150ms linear;
|
||||
}
|
||||
|
||||
.link:focus,
|
||||
.link:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.link:focus,
|
||||
.link:hover .linkInner {
|
||||
transform: translateY(0);
|
||||
border-color: rgba(255, 255, 255, 0.625);
|
||||
}
|
||||
|
||||
.linkInner {
|
||||
padding: 0.375em 1em;
|
||||
border: 2px solid rgba(255, 255, 255, 0);
|
||||
transition: transform 300ms cubic-bezier(0, 0.4, 0.6, 1), border-color 1s linear;
|
||||
transform: translateY(25%);
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.tags {
|
||||
font-size: var(--f-d2);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
color: var(--c-yellow);
|
||||
text-transform: uppercase;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.tag:nth-of-type(1n) {
|
||||
color: var(--c-green);
|
||||
}
|
||||
.tag:nth-of-type(2n) {
|
||||
color: var(--c-orange);
|
||||
}
|
||||
.tag:nth-of-type(3n) {
|
||||
color: var(--c-blue);
|
||||
}
|
||||
.tag:nth-of-type(4n) {
|
||||
color: var(--c-pink);
|
||||
.title {
|
||||
border-radius: 0.9375rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
62
examples/portfolio/src/components/Skills.astro
Normal file
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
import Icon from './Icon.astro';
|
||||
---
|
||||
|
||||
<section class="box skills">
|
||||
<div class="stack gap-2 lg:gap-4">
|
||||
<Icon icon="terminal-window" color="var(--accent-regular)" size="2.5rem" gradient />
|
||||
<h2>Full Stack</h2>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.</p>
|
||||
</div>
|
||||
<div class="stack gap-2 lg:gap-4">
|
||||
<Icon icon="trophy" color="var(--accent-regular)" size="2.5rem" gradient />
|
||||
<h2>Industry Leader</h2>
|
||||
<p>Neque viverra justo nec ultrices dui. Est ultricies integer quis auctor elit.</p>
|
||||
</div>
|
||||
<div class="stack gap-2 lg:gap-4">
|
||||
<Icon icon="strategy" color="var(--accent-regular)" size="2.5rem" gradient />
|
||||
<h2>Strategy-Minded</h2>
|
||||
<p>Urna porttitor rhoncus dolor purus non enim praesent ornare.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.box {
|
||||
border: 1px solid var(--gray-800);
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.5rem;
|
||||
background-color: var(--gray-999_40);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.skills {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3rem;
|
||||
}
|
||||
|
||||
.skills h2 {
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
|
||||
.skills p {
|
||||
color: var(--gray-400);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.box {
|
||||
border-radius: 1.5rem;
|
||||
padding: 2.5rem;
|
||||
}
|
||||
|
||||
.skills {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 5rem;
|
||||
}
|
||||
|
||||
.skills h2 {
|
||||
font-size: var(--text-2xl);
|
||||
}
|
||||
}
|
||||
</style>
|
95
examples/portfolio/src/components/ThemeToggle.astro
Normal file
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
import Icon from './Icon.astro';
|
||||
---
|
||||
|
||||
<theme-toggle>
|
||||
<button>
|
||||
<span class="sr-only">Dark theme</span>
|
||||
<span class="icon light"><Icon icon="sun" /></span>
|
||||
<span class="icon dark"><Icon icon="moon-stars" /></span>
|
||||
</button>
|
||||
</theme-toggle>
|
||||
|
||||
<style>
|
||||
button {
|
||||
display: flex;
|
||||
border: 0;
|
||||
border-radius: 999rem;
|
||||
padding: 0;
|
||||
background-color: var(--gray-999);
|
||||
box-shadow: inset 0 0 0 1px var(--accent-overlay);
|
||||
cursor: pointer;
|
||||
/* Outline visible only to high contrast users */
|
||||
outline: 1px solid transparent;
|
||||
}
|
||||
|
||||
.icon {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding: 0.5rem;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
font-size: 1rem;
|
||||
color: var(--accent-overlay);
|
||||
}
|
||||
|
||||
.icon.light::before {
|
||||
content: '';
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: var(--accent-regular);
|
||||
border-radius: 999rem;
|
||||
}
|
||||
|
||||
:global(.theme-dark) .icon.light::before {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
:global(.theme-dark) .icon.dark,
|
||||
:global(html:not(.theme-dark)) .icon.light,
|
||||
button[aria-pressed='false'] .icon.light {
|
||||
color: var(--accent-text-over);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.icon,
|
||||
.icon.light::before {
|
||||
transition: transform var(--theme-transition), color var(--theme-transition);
|
||||
}
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.icon.light::before {
|
||||
background-color: SelectedItem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
class ThemeToggle extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
const button = this.querySelector('button')!;
|
||||
|
||||
/** Set the theme to dark/light mode. */
|
||||
const setTheme = (dark: boolean) => {
|
||||
document.documentElement.classList[dark ? 'add' : 'remove']('theme-dark');
|
||||
button.setAttribute('aria-pressed', String(dark));
|
||||
};
|
||||
|
||||
// Toggle the theme when a user clicks the button.
|
||||
button.addEventListener('click', () => setTheme(!this.isDark()));
|
||||
|
||||
// Initialize button state to reflect current theme.
|
||||
setTheme(this.isDark());
|
||||
}
|
||||
|
||||
isDark() {
|
||||
return document.documentElement.classList.contains('theme-dark');
|
||||
}
|
||||
}
|
||||
customElements.define('theme-toggle', ThemeToggle);
|
||||
</script>
|
14
examples/portfolio/src/content/config.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { defineCollection, z } from 'astro:content';
|
||||
|
||||
export const collections = {
|
||||
work: defineCollection({
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
publishDate: z.coerce.date(),
|
||||
tags: z.array(z.string()),
|
||||
img: z.string(),
|
||||
img_alt: z.string().optional(),
|
||||
}),
|
||||
}),
|
||||
};
|
23
examples/portfolio/src/content/work/bloom-box.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
title: Bloom Box
|
||||
publishDate: 2019-12-01 00:00:00
|
||||
img: /assets/stock-2.jpg
|
||||
img_alt: A bright pink sheet of paper used to wrap flowers curves in front of rich blue background
|
||||
description: |
|
||||
We paired with a cutting-edge music API and a team of horticulturalists
|
||||
to build AI-generated playlists that maximize houseplant health.
|
||||
tags:
|
||||
- Dev
|
||||
- Branding
|
||||
- Backend
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur posuere commodo venenatis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam non ligula vel metus efficitur hendrerit. In hac habitasse platea dictumst. Praesent et mauris ut mi dapibus semper. Curabitur tortor justo, efficitur sit amet pretium cursus, porta eget odio. Cras ac venenatis dolor. Donec laoreet posuere malesuada. Curabitur nec mi tempor, placerat leo sit amet, tincidunt est. Quisque pellentesque venenatis magna, eget tristique nibh pulvinar in. Vestibulum vitae volutpat arcu. Aenean ut malesuada odio, sit amet pellentesque odio. Suspendisse nunc elit, blandit nec hendrerit non, aliquet at magna. Donec id leo ut nulla sagittis sodales.
|
||||
|
||||
Integer vitae nibh elit. Suspendisse eget urna eu neque bibendum pharetra. Sed interdum lectus sem, in pulvinar magna dignissim vel. Quisque maximus at urna nec laoreet. Suspendisse potenti. Vestibulum rhoncus sem ut mi pellentesque, in vestibulum erat blandit. Aliquam sodales dui ac maximus consectetur. Duis quis est vehicula, imperdiet nisl nec, fermentum erat. Duis tortor diam, pharetra eu euismod in, vehicula non eros. Curabitur facilisis dui at erat ultrices gravida. In at nunc ultricies, pulvinar mi vel, sagittis mauris. Praesent pharetra posuere purus ac imperdiet. Nulla facilisi.
|
||||
|
||||
Sed pulvinar porttitor mi in ultricies. Etiam non dolor gravida eros pulvinar pellentesque et dictum ex. Proin eu ornare ligula, sed condimentum dui. Vivamus tincidunt tellus mi, sed semper ipsum pharetra a. Suspendisse sollicitudin at sapien nec volutpat. Etiam justo urna, laoreet ac lacus sed, ultricies facilisis dolor. Integer posuere, metus vel viverra gravida, risus elit ornare magna, id feugiat erat risus ullamcorper libero. Proin vitae diam auctor, laoreet lorem vitae, varius tellus.
|
||||
|
||||
Mauris sed eros in ex maximus volutpat. Suspendisse potenti. Donec lacinia justo consectetur sagittis tempor. Proin ullamcorper nisi vitae auctor rhoncus. Sed tristique aliquam augue. Pellentesque vitae fringilla ligula. Nulla arcu elit, efficitur eu nunc malesuada, eleifend tincidunt orci. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer mattis orci in bibendum ultricies. Quisque a dui erat. Phasellus et vulputate ipsum. Proin metus ex, lobortis nec ornare eget, bibendum ut sapien. Aliquam in dolor lobortis, aliquam tellus a, congue augue. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
Aenean pretium purus augue, ut bibendum erat convallis quis. Cras condimentum quis velit ac mollis. Suspendisse non purus fringilla, venenatis nisl porta, finibus odio. Curabitur aliquet metus faucibus libero interdum euismod. Morbi sed magna nisl. Morbi odio nibh, facilisis vel sapien eu, tempus tincidunt erat. Nullam erat velit, sagittis at purus quis, tristique scelerisque tortor. Pellentesque lacinia tortor id est aliquam viverra. Vestibulum et diam ac ipsum mollis fringilla.
|
22
examples/portfolio/src/content/work/h20.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
title: h2.0
|
||||
publishDate: 2019-10-02 00:00:00
|
||||
img: /assets/stock-4.jpg
|
||||
img_alt: Soft pink and baby blue water ripples together in a subtle texture.
|
||||
description: |
|
||||
We developed brand positioning and design assets for the launch
|
||||
of a new colored water product.
|
||||
tags:
|
||||
- Design
|
||||
- Branding
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur posuere commodo venenatis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam non ligula vel metus efficitur hendrerit. In hac habitasse platea dictumst. Praesent et mauris ut mi dapibus semper. Curabitur tortor justo, efficitur sit amet pretium cursus, porta eget odio. Cras ac venenatis dolor. Donec laoreet posuere malesuada. Curabitur nec mi tempor, placerat leo sit amet, tincidunt est. Quisque pellentesque venenatis magna, eget tristique nibh pulvinar in. Vestibulum vitae volutpat arcu. Aenean ut malesuada odio, sit amet pellentesque odio. Suspendisse nunc elit, blandit nec hendrerit non, aliquet at magna. Donec id leo ut nulla sagittis sodales.
|
||||
|
||||
Integer vitae nibh elit. Suspendisse eget urna eu neque bibendum pharetra. Sed interdum lectus sem, in pulvinar magna dignissim vel. Quisque maximus at urna nec laoreet. Suspendisse potenti. Vestibulum rhoncus sem ut mi pellentesque, in vestibulum erat blandit. Aliquam sodales dui ac maximus consectetur. Duis quis est vehicula, imperdiet nisl nec, fermentum erat. Duis tortor diam, pharetra eu euismod in, vehicula non eros. Curabitur facilisis dui at erat ultrices gravida. In at nunc ultricies, pulvinar mi vel, sagittis mauris. Praesent pharetra posuere purus ac imperdiet. Nulla facilisi.
|
||||
|
||||
Sed pulvinar porttitor mi in ultricies. Etiam non dolor gravida eros pulvinar pellentesque et dictum ex. Proin eu ornare ligula, sed condimentum dui. Vivamus tincidunt tellus mi, sed semper ipsum pharetra a. Suspendisse sollicitudin at sapien nec volutpat. Etiam justo urna, laoreet ac lacus sed, ultricies facilisis dolor. Integer posuere, metus vel viverra gravida, risus elit ornare magna, id feugiat erat risus ullamcorper libero. Proin vitae diam auctor, laoreet lorem vitae, varius tellus.
|
||||
|
||||
Mauris sed eros in ex maximus volutpat. Suspendisse potenti. Donec lacinia justo consectetur sagittis tempor. Proin ullamcorper nisi vitae auctor rhoncus. Sed tristique aliquam augue. Pellentesque vitae fringilla ligula. Nulla arcu elit, efficitur eu nunc malesuada, eleifend tincidunt orci. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer mattis orci in bibendum ultricies. Quisque a dui erat. Phasellus et vulputate ipsum. Proin metus ex, lobortis nec ornare eget, bibendum ut sapien. Aliquam in dolor lobortis, aliquam tellus a, congue augue. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
Aenean pretium purus augue, ut bibendum erat convallis quis. Cras condimentum quis velit ac mollis. Suspendisse non purus fringilla, venenatis nisl porta, finibus odio. Curabitur aliquet metus faucibus libero interdum euismod. Morbi sed magna nisl. Morbi odio nibh, facilisis vel sapien eu, tempus tincidunt erat. Nullam erat velit, sagittis at purus quis, tristique scelerisque tortor. Pellentesque lacinia tortor id est aliquam viverra. Vestibulum et diam ac ipsum mollis fringilla.
|
35
examples/portfolio/src/content/work/markdown-mystery-tour.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
title: Markdown Mystery Tour
|
||||
publishDate: 2020-03-02 00:00:00
|
||||
img: /assets/stock-1.jpg
|
||||
img_alt: Iridescent ripples of a bright blue and pink liquid
|
||||
description: |
|
||||
We designed a whodunnit-style game to introduce Markdown formatting. Suspense — suspicion — syntax!
|
||||
tags:
|
||||
- Design
|
||||
- Dev
|
||||
- User Testing
|
||||
---
|
||||
|
||||
## Level-two heading
|
||||
|
||||
> Tell me and I forget. Teach me and I remember. Involve me and I learn.
|
||||
|
||||
Lorem ipsum dolor sit amet, <a href="https://astro.build/">Astro</a> makes people happy. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Proin nibh nisl condimentum id venenatis a condimentum vitae. Dapibus ultrices in iaculis nunc. Arcu odio ut sem nulla pharetra diam sit amet. Diam quis enim lobortis scelerisque fermentum dui faucibus in ornare.
|
||||
|
||||
Arcu dui vivamus arcu felis bibendum ut tristique et egestas. Eget gravida cum sociis natoque penatibus. Cras fermentum odio eu feugiat pretium nibh. Proin nibh nisl condimentum id venenatis. Porta nibh venenatis cras sed felis eget velit. Id diam vel quam elementum pulvinar etiam non.
|
||||
|
||||
### Level-three heading
|
||||
|
||||
Ultrices tincidunt arcu non sodales neque sodales ut. Sed enim ut sem viverra aliquet eget sit amet. Lacus luctus accumsan tortor posuere ac ut consequat semper viverra. Viverra accumsan in nisl nisi scelerisque eu ultrices. In massa tempor nec feugiat nisl pretium fusce.
|
||||
|
||||
### Level-three heading
|
||||
|
||||
Sed pulvinar porttitor mi in ultricies. Etiam non dolor gravida eros pulvinar pellentesque et dictum ex. Proin eu ornare ligula, sed condimentum dui. Vivamus tincidunt tellus mi, sed semper ipsum pharetra a. Suspendisse sollicitudin at sapien nec volutpat. Etiam justo urna, laoreet ac lacus sed, ultricies facilisis dolor. Integer posuere, metus vel viverra gravida, risus elit ornare magna, id feugiat erat risus ullamcorper libero. Proin vitae diam auctor, laoreet lorem vitae, varius tellus.
|
||||
|
||||
Aenean pretium purus augue, ut bibendum erat convallis quis. Cras condimentum quis velit ac mollis. Suspendisse non purus fringilla, venenatis nisl porta, finibus odio. Curabitur aliquet metus faucibus libero interdum euismod. Morbi sed magna nisl. Morbi odio nibh, facilisis vel sapien eu, tempus tincidunt erat. Nullam erat velit, sagittis at purus quis, tristique scelerisque tortor. Pellentesque lacinia tortor id est aliquam viverra. Vestibulum et diam ac ipsum mollis fringilla.
|
||||
|
||||
#### Level-four heading
|
||||
|
||||
- We noted this
|
||||
- And also this other point
|
22
examples/portfolio/src/content/work/nested/duvet-genius.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
title: Duvet Genius
|
||||
publishDate: 2020-03-04 00:00:00
|
||||
img: /assets/stock-3.jpg
|
||||
img_alt: Pearls of silky soft white cotton, bubble up under vibrant lighting
|
||||
description: |
|
||||
We developed a virtual showcase for the softest bedding imaginable.
|
||||
tags:
|
||||
- Design
|
||||
- Dev
|
||||
- Branding
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur posuere commodo venenatis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam non ligula vel metus efficitur hendrerit. In hac habitasse platea dictumst. Praesent et mauris ut mi dapibus semper. Curabitur tortor justo, efficitur sit amet pretium cursus, porta eget odio. Cras ac venenatis dolor. Donec laoreet posuere malesuada. Curabitur nec mi tempor, placerat leo sit amet, tincidunt est. Quisque pellentesque venenatis magna, eget tristique nibh pulvinar in. Vestibulum vitae volutpat arcu. Aenean ut malesuada odio, sit amet pellentesque odio. Suspendisse nunc elit, blandit nec hendrerit non, aliquet at magna. Donec id leo ut nulla sagittis sodales.
|
||||
|
||||
Integer vitae nibh elit. Suspendisse eget urna eu neque bibendum pharetra. Sed interdum lectus sem, in pulvinar magna dignissim vel. Quisque maximus at urna nec laoreet. Suspendisse potenti. Vestibulum rhoncus sem ut mi pellentesque, in vestibulum erat blandit. Aliquam sodales dui ac maximus consectetur. Duis quis est vehicula, imperdiet nisl nec, fermentum erat. Duis tortor diam, pharetra eu euismod in, vehicula non eros. Curabitur facilisis dui at erat ultrices gravida. In at nunc ultricies, pulvinar mi vel, sagittis mauris. Praesent pharetra posuere purus ac imperdiet. Nulla facilisi.
|
||||
|
||||
Sed pulvinar porttitor mi in ultricies. Etiam non dolor gravida eros pulvinar pellentesque et dictum ex. Proin eu ornare ligula, sed condimentum dui. Vivamus tincidunt tellus mi, sed semper ipsum pharetra a. Suspendisse sollicitudin at sapien nec volutpat. Etiam justo urna, laoreet ac lacus sed, ultricies facilisis dolor. Integer posuere, metus vel viverra gravida, risus elit ornare magna, id feugiat erat risus ullamcorper libero. Proin vitae diam auctor, laoreet lorem vitae, varius tellus.
|
||||
|
||||
Mauris sed eros in ex maximus volutpat. Suspendisse potenti. Donec lacinia justo consectetur sagittis tempor. Proin ullamcorper nisi vitae auctor rhoncus. Sed tristique aliquam augue. Pellentesque vitae fringilla ligula. Nulla arcu elit, efficitur eu nunc malesuada, eleifend tincidunt orci. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer mattis orci in bibendum ultricies. Quisque a dui erat. Phasellus et vulputate ipsum. Proin metus ex, lobortis nec ornare eget, bibendum ut sapien. Aliquam in dolor lobortis, aliquam tellus a, congue augue. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
Aenean pretium purus augue, ut bibendum erat convallis quis. Cras condimentum quis velit ac mollis. Suspendisse non purus fringilla, venenatis nisl porta, finibus odio. Curabitur aliquet metus faucibus libero interdum euismod. Morbi sed magna nisl. Morbi odio nibh, facilisis vel sapien eu, tempus tincidunt erat. Nullam erat velit, sagittis at purus quis, tristique scelerisque tortor. Pellentesque lacinia tortor id est aliquam viverra. Vestibulum et diam ac ipsum mollis fringilla.
|
1
examples/portfolio/src/env.d.ts
vendored
|
@ -1 +1,2 @@
|
|||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
||||
|
|
113
examples/portfolio/src/layouts/BaseLayout.astro
Normal file
|
@ -0,0 +1,113 @@
|
|||
---
|
||||
// Learn about using Astro layouts:
|
||||
// https://docs.astro.build/en/core-concepts/layouts/
|
||||
|
||||
// Component Imports
|
||||
import MainHead from '../components/MainHead.astro';
|
||||
import Nav from '../components/Nav.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
|
||||
interface Props {
|
||||
title?: string | undefined;
|
||||
description?: string | undefined;
|
||||
}
|
||||
|
||||
const { title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<MainHead title={title} description={description} />
|
||||
</head>
|
||||
<body>
|
||||
<div class="stack backgrounds">
|
||||
<Nav />
|
||||
<slot />
|
||||
<Footer />
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Add “loaded” class once the document has completely loaded.
|
||||
addEventListener('load', () => document.documentElement.classList.add('loaded'));
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--_placeholder-bg: linear-gradient(transparent, transparent);
|
||||
--bg-image-main: url('/assets/backgrounds/bg-main-light-800w.jpg');
|
||||
--bg-image-main-curves: url('/assets/backgrounds/bg-main-light.svg');
|
||||
--bg-image-subtle-1: var(--_placeholder-bg);
|
||||
--bg-image-subtle-2: var(--_placeholder-bg);
|
||||
--bg-image-footer: var(--_placeholder-bg);
|
||||
--bg-svg-blend-mode: overlay;
|
||||
--bg-blend-mode: darken;
|
||||
--bg-image-aspect-ratio: 2.25;
|
||||
--bg-scale: 1.68;
|
||||
--bg-aspect-ratio: calc(var(--bg-image-aspect-ratio) / var(--bg-scale));
|
||||
--bg-gradient-size: calc(var(--bg-scale) * 100%);
|
||||
}
|
||||
|
||||
:root.theme-dark {
|
||||
--bg-image-main: url('/assets/backgrounds/bg-main-dark-800w.jpg');
|
||||
--bg-image-main-curves: url('/assets/backgrounds/bg-main-dark.svg');
|
||||
--bg-svg-blend-mode: darken;
|
||||
--bg-blend-mode: lighten;
|
||||
}
|
||||
|
||||
/* These backgrounds are displayed below the fold, so we lazy load them
|
||||
once the `.loaded` class has been set. */
|
||||
:root.loaded {
|
||||
--bg-image-subtle-1: url('/assets/backgrounds/bg-subtle-1-light-800w.jpg');
|
||||
--bg-image-subtle-2: url('/assets/backgrounds/bg-subtle-2-light-800w.jpg');
|
||||
--bg-image-footer: url('/assets/backgrounds/bg-footer-light-800w.jpg');
|
||||
}
|
||||
:root.loaded.theme-dark {
|
||||
--bg-image-subtle-1: url('/assets/backgrounds/bg-subtle-1-dark-800w.jpg');
|
||||
--bg-image-subtle-2: url('/assets/backgrounds/bg-subtle-2-dark-800w.jpg');
|
||||
--bg-image-footer: url('/assets/backgrounds/bg-footer-dark-800w.jpg');
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
:root {
|
||||
--bg-scale: 1;
|
||||
--bg-image-main: url('/assets/backgrounds/bg-main-light-1440w.jpg');
|
||||
}
|
||||
:root.theme-dark {
|
||||
--bg-image-main: url('/assets/backgrounds/bg-main-dark-1440w.jpg');
|
||||
}
|
||||
|
||||
:root.loaded {
|
||||
--bg-image-subtle-1: url('/assets/backgrounds/bg-subtle-1-light-1440w.jpg');
|
||||
--bg-image-subtle-2: url('/assets/backgrounds/bg-subtle-2-light-1440w.jpg');
|
||||
--bg-image-footer: url('/assets/backgrounds/bg-footer-light-1440w.jpg');
|
||||
}
|
||||
:root.loaded.theme-dark {
|
||||
--bg-image-subtle-1: url('/assets/backgrounds/bg-subtle-1-dark-1440w.jpg');
|
||||
--bg-image-subtle-2: url('/assets/backgrounds/bg-subtle-2-dark-1440w.jpg');
|
||||
--bg-image-footer: url('/assets/backgrounds/bg-footer-dark-1440w.jpg');
|
||||
}
|
||||
}
|
||||
|
||||
.backgrounds {
|
||||
min-height: 100%;
|
||||
isolation: isolate;
|
||||
background:
|
||||
/*noise*/ url('/assets/backgrounds/noise.png') top center/220px repeat,
|
||||
/*footer*/ var(--bg-image-footer) bottom center/var(--bg-gradient-size) no-repeat,
|
||||
/*header1*/ var(--bg-image-main-curves) top center/var(--bg-gradient-size) no-repeat,
|
||||
/*header2*/ var(--bg-image-main) top center/var(--bg-gradient-size) no-repeat,
|
||||
/*base*/ var(--gray-999);
|
||||
background-blend-mode: /*noise*/ overlay, /*footer*/ var(--bg-blend-mode),
|
||||
/*header1*/ var(--bg-svg-blend-mode), /*header2*/ normal, /*base*/ normal;
|
||||
}
|
||||
@media (forced-colors: active) {
|
||||
/* Deactivate custom backgrounds for high contrast users. */
|
||||
.backgrounds {
|
||||
background: none;
|
||||
background-blend-mode: none;
|
||||
--bg-gradient-size: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
|
@ -1,107 +0,0 @@
|
|||
---
|
||||
import MainHead from '../components/MainHead.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import Nav from '../components/Nav.astro';
|
||||
import type { Project } from '../types';
|
||||
|
||||
interface Props {
|
||||
content: Project;
|
||||
}
|
||||
|
||||
const { content } = Astro.props;
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<MainHead title={content.title} description={content.description} />
|
||||
<style>
|
||||
.hero {
|
||||
padding: 8rem;
|
||||
display: flex;
|
||||
background-color: var(--t-fg);
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
min-height: 25vw;
|
||||
color: white;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tag {
|
||||
margin-left: 0.5em;
|
||||
margin-right: 0.5em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.tag:nth-of-type(1n) {
|
||||
color: var(--c-green);
|
||||
}
|
||||
.tag:nth-of-type(2n) {
|
||||
color: var(--c-orange);
|
||||
}
|
||||
.tag:nth-of-type(3n) {
|
||||
color: var(--c-blue);
|
||||
}
|
||||
.tag:nth-of-type(4n) {
|
||||
color: var(--c-pink);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: var(--f-u10);
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.0625em;
|
||||
}
|
||||
|
||||
.leadIn {
|
||||
padding-top: 4em;
|
||||
color: var(--t-bg);
|
||||
background-color: var(--t-fg);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
font-weight: 300;
|
||||
font-size: var(--f-u3);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: var(--f-u1);
|
||||
line-height: 2.2;
|
||||
max-width: 50em;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
padding-bottom: 4em;
|
||||
margin-bottom: 4em;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<Nav />
|
||||
<header style={`background-image:url(${content.img})`} class="hero">
|
||||
<h1 class="title">{content.title}</h1>
|
||||
</header>
|
||||
<div class="leadIn">
|
||||
<div class="wrapper">
|
||||
{content.tags.map((t) => <span class="tag">{t}</span>)}
|
||||
<h3 class="tagline">{content.description}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
<div class="content"><slot /></div>
|
||||
</div>
|
||||
<footer>
|
||||
<a href="/projects" class="button">View More</a>
|
||||
</footer>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
|
@ -1,25 +1,8 @@
|
|||
---
|
||||
import MainHead from '../components/MainHead.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import Nav from '../components/Nav.astro';
|
||||
import Hero from '../components/Hero.astro';
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<MainHead title="Not Found" />
|
||||
<style>
|
||||
.wrapper {
|
||||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<Nav />
|
||||
<div class="wrapper">
|
||||
<h1>Page Not Found</h1>
|
||||
<p>Not found</p>
|
||||
</div>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
<BaseLayout title="Not Found" description="404 Error — this page was not found">
|
||||
<Hero title="Page Not Found" tagline="Not found" />
|
||||
</BaseLayout>
|
||||
|
|
|
@ -1,77 +1,120 @@
|
|||
---
|
||||
import MainHead from '../components/MainHead.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import Nav from '../components/Nav.astro';
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
|
||||
import ContactCTA from '../components/ContactCTA.astro';
|
||||
import Hero from '../components/Hero.astro';
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<MainHead title="About | Jeanine White" description="About Jeanine White Lorem Ipsum" />
|
||||
<style>
|
||||
.heroImg {
|
||||
max-height: 24rem;
|
||||
object-fit: cover;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.heroImg img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bio {
|
||||
font-size: var(--f-u1);
|
||||
line-height: 2;
|
||||
margin-top: 4em;
|
||||
}
|
||||
|
||||
.wrapper.bio {
|
||||
max-width: 50em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<Nav />
|
||||
<div class="wrapper">
|
||||
<h1>About Jeanine</h1>
|
||||
<div class="heroImg">
|
||||
<BaseLayout title="About | Jeanine White" description="About Jeanine White Lorem Ipsum">
|
||||
<div class="stack gap-20">
|
||||
<main class="wrapper about">
|
||||
<Hero
|
||||
title="About"
|
||||
tagline="Thanks for stopping by. Read below to learn more about myself and my background."
|
||||
>
|
||||
<img
|
||||
width="1400"
|
||||
height="350"
|
||||
src="https://images.unsplash.com/photo-1581977012607-4091712d36f9?auto=format&fit=crop&w=1400&h=350&q=75"
|
||||
width="1553"
|
||||
height="873"
|
||||
src="/assets/at-work.jpg"
|
||||
alt="Jeanine White at work with a colleague"
|
||||
/>
|
||||
</div>
|
||||
<div class="bio wrapper">
|
||||
<p>
|
||||
Cream cheese say cheese stinking bishop. Brie fondue hard cheese bocconcini feta camembert
|
||||
de normandie babybel airedale. Red leicester swiss manchego mascarpone pepper jack
|
||||
airedale fromage frais ricotta. Cheese and biscuits cauliflower cheese boursin.
|
||||
</p>
|
||||
<p>
|
||||
Pepper jack cheesy feet cheese slices. Halloumi port-salut queso caerphilly roquefort
|
||||
cheese slices cheesy feet rubber cheese. Cheese slices smelly cheese pecorino macaroni
|
||||
cheese feta blue castello roquefort edam. Babybel pepper jack airedale cheddar fromage
|
||||
frais manchego.
|
||||
</p>
|
||||
<p>
|
||||
Cauliflower cheese lancashire macaroni cheese. Cheeseburger babybel cheese on toast
|
||||
airedale cauliflower cheese who moved my cheese roquefort paneer. Stinking bishop cheddar
|
||||
taleggio port-salut port-salut stinking bishop cheesy grin babybel. Blue castello feta
|
||||
everyone loves brie.
|
||||
</p>
|
||||
<p>
|
||||
Goat squirty cheese cut the cheese. Cheese and wine cheddar fondue airedale cottage cheese
|
||||
camembert de normandie feta babybel. Rubber cheese melted cheese pecorino port-salut
|
||||
fondue gouda cheese on toast cheesy feet. Feta edam everyone loves cheese strings
|
||||
camembert de normandie.
|
||||
</p>
|
||||
<p>
|
||||
Caerphilly monterey jack goat. Squirty cheese cheesy grin hard cheese cheese strings
|
||||
cheese and biscuits croque monsieur smelly cheese danish fontina. Swiss cheese triangles
|
||||
everyone loves mascarpone cheese on toast who moved my cheese lancashire cheeseburger.
|
||||
Fromage frais fromage frais cheese and biscuits stinking bishop cauliflower cheese.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
</Hero>
|
||||
|
||||
<section>
|
||||
<h2 class="section-title">Background</h2>
|
||||
<div class="content">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, <a href="https://astro.build/">Astro</a> makes people happy.
|
||||
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Proin nibh nisl condimentum
|
||||
id venenatis a condimentum vitae. Dapibus ultrices in iaculis nunc. Arcu odio ut sem nulla
|
||||
pharetra diam sit amet. Diam quis enim lobortis scelerisque fermentum dui faucibus in ornare.
|
||||
</p>
|
||||
<p>
|
||||
Arcu dui vivamus arcu felis bibendum ut tristique et egestas. Eget gravida cum sociis
|
||||
natoque penatibus. Cras fermentum odio eu feugiat pretium nibh. Proin nibh nisl
|
||||
condimentum id venenatis. Porta nibh venenatis cras sed felis eget velit. Id diam vel
|
||||
quam elementum pulvinar etiam non.
|
||||
</p>
|
||||
<p>
|
||||
Ultrices tincidunt arcu non sodales neque sodales ut. Sed enim ut sem viverra aliquet
|
||||
eget sit amet. Lacus luctus accumsan tortor posuere ac ut consequat semper viverra.
|
||||
Viverra accumsan in nisl nisi scelerisque eu ultrices. In massa tempor nec feugiat nisl
|
||||
pretium fusce.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title">Education</h2>
|
||||
<div class="content">
|
||||
<p>Corporis voluptates tenetur laudantium.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="section-title">Skills</h2>
|
||||
<div class="content">
|
||||
<p>officia unde omnis</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<ContactCTA />
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
.about {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3.5rem;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-top: 1.5rem;
|
||||
border-radius: 1.5rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
color: var(--gray-200);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
grid-column-start: 1;
|
||||
font-size: var(--text-xl);
|
||||
color: var(--gray-0);
|
||||
}
|
||||
|
||||
.content {
|
||||
grid-column: 2 / 4;
|
||||
}
|
||||
|
||||
.content :global(a) {
|
||||
text-decoration: 1px solid underline transparent;
|
||||
text-underline-offset: 0.25em;
|
||||
transition: text-decoration-color var(--theme-transition);
|
||||
}
|
||||
|
||||
.content :global(a:hover),
|
||||
.content :global(a:focus) {
|
||||
text-decoration-color: currentColor;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.about {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 60% 1fr;
|
||||
}
|
||||
|
||||
.about > :global(:first-child) {
|
||||
grid-column-start: 2;
|
||||
}
|
||||
|
||||
section {
|
||||
display: contents;
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,252 +1,250 @@
|
|||
---
|
||||
// Component Imports
|
||||
import MainHead from '../components/MainHead.astro';
|
||||
import Nav from '../components/Nav.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import PortfolioPreview from '../components/PortfolioPreview.astro';
|
||||
import type { Project } from '../types';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
// Data Fetching: List all Markdown posts in the repo.
|
||||
const projects = await Astro.glob<Project>('./project/**/*.md');
|
||||
const featuredProject = projects[0]!;
|
||||
// Layout import — provides basic page elements: <head>, <nav>, <footer> etc.
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
|
||||
// Component Imports
|
||||
import CallToAction from '../components/CallToAction.astro';
|
||||
import Grid from '../components/Grid.astro';
|
||||
import Hero from '../components/Hero.astro';
|
||||
import Icon from '../components/Icon.astro';
|
||||
import Pill from '../components/Pill.astro';
|
||||
import PortfolioPreview from '../components/PortfolioPreview.astro';
|
||||
|
||||
// Page section components
|
||||
import ContactCTA from '../components/ContactCTA.astro';
|
||||
import Skills from '../components/Skills.astro';
|
||||
|
||||
// Content Fetching: List four most recent work projects
|
||||
const projects = (await getCollection('work'))
|
||||
.sort((a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf())
|
||||
.slice(0, 4);
|
||||
|
||||
// Full Astro Component Syntax:
|
||||
// https://docs.astro.build/core-concepts/astro-components/
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<MainHead
|
||||
title="Jeanine White: Personal Site"
|
||||
description="Jeanine White: Developer, Speaker, and Writer..."
|
||||
/>
|
||||
<style>
|
||||
.wrapper {
|
||||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.hero {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media (min-width: 750px) {
|
||||
.hero {
|
||||
height: 45vw;
|
||||
}
|
||||
}
|
||||
|
||||
.img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.gradient,
|
||||
.gradient2 {
|
||||
background-image: url('/assets/mesh-gradient.jpg');
|
||||
background-size: cover;
|
||||
pointer-events: none;
|
||||
mix-blend-mode: screen;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.gradient2 {
|
||||
mix-blend-mode: multiply;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
@media (min-width: 750px) {
|
||||
.overlay {
|
||||
padding-left: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 900;
|
||||
font-size: var(--f-u6);
|
||||
margin-bottom: 0.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 750px) {
|
||||
.title {
|
||||
font-size: var(--f-u12);
|
||||
}
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-gap: 2rem;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.grid {
|
||||
grid-template-columns: 2fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-weight: 700;
|
||||
font-size: var(--f-u8);
|
||||
margin-top: 4rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.roles {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em;
|
||||
font-size: var(--f-d1);
|
||||
}
|
||||
|
||||
.role {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
font-weight: 900;
|
||||
color: var(--t-bg);
|
||||
background-color: var(--t-fg);
|
||||
padding: 0.25em 0.5em;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
@media (min-width: 750px) {
|
||||
.role {
|
||||
font-size: var(--f-u3);
|
||||
}
|
||||
}
|
||||
|
||||
.role:nth-of-type(1) .invert {
|
||||
background-color: var(--c-pink);
|
||||
}
|
||||
|
||||
.role:nth-of-type(2) .invert {
|
||||
background-color: var(--c-blue);
|
||||
}
|
||||
|
||||
.role:nth-of-type(3) .invert {
|
||||
background-color: var(--c-green);
|
||||
}
|
||||
|
||||
.role:hover .invert {
|
||||
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
|
||||
}
|
||||
|
||||
.invert {
|
||||
position: absolute;
|
||||
color: var(--t-fg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
clip-path: polygon(0% 100%, 100% 100%, 100% 200%, 0% 200%);
|
||||
transition: clip-path cubic-bezier(0.4, 0, 0.5, 1) 150ms;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: var(--f-u1);
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
display: block;
|
||||
font-weight: 400;
|
||||
font-size: var(--f-d6);
|
||||
letter-spacing: -0.0625em;
|
||||
}
|
||||
|
||||
.bio {
|
||||
line-height: 2;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.bio > span:first-of-type {
|
||||
line-height: 1;
|
||||
margin-bottom: 0.5em;
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
font-size: var(--f-u4);
|
||||
}
|
||||
|
||||
.buttonContainer {
|
||||
text-align: center;
|
||||
margin-top: 2em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<Nav />
|
||||
<header class="hero">
|
||||
<img
|
||||
width="1600"
|
||||
height="1131"
|
||||
class="img"
|
||||
src="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75"
|
||||
srcset="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 800w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1200&q=75 1200w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1600&q=75 1600w,https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=2400&q=75 2400w"
|
||||
sizes="(max-width: 800px) 800px, (max-width: 1200px) 1200px, (max-width: 1600px) 1600px, (max-width: 2400px) 2400px, 1200px"
|
||||
/>
|
||||
<div class="gradient"></div>
|
||||
<div class="gradient2"></div>
|
||||
<div class="overlay">
|
||||
<h1 class="title">
|
||||
<small class="subtitle">The personal site of</small>Jeanine White
|
||||
</h1>
|
||||
<div class="roles">
|
||||
<span class="role">👩💻 Developer <span class="invert">👩💻 Developer</span></span>
|
||||
<span class="role">🎤 Speaker <span class="invert">🎤 Speaker</span></span>
|
||||
<span class="role">✏️ Writer <span class="invert">✏️ Writer</span></span>
|
||||
</div>
|
||||
<p class="desc">Lover of dogs, roadtrips, and poetry.</p>
|
||||
</div>
|
||||
</header>
|
||||
<main class="wrapper">
|
||||
<div class="grid">
|
||||
<div class="section">
|
||||
<h3 class="sectionTitle">Selected Work</h3>
|
||||
<PortfolioPreview project={featuredProject} />
|
||||
<div class="buttonContainer">
|
||||
<a href="/projects/" class="button">View All</a>
|
||||
<BaseLayout>
|
||||
<div class="stack gap-20 lg:gap-48">
|
||||
<div class="wrapper stack gap-8 lg:gap-20">
|
||||
<header class="hero">
|
||||
<Hero
|
||||
title="Hello, my name is Jeanine White"
|
||||
tagline="I am a Creative Developer who is currently based in Portland, Oregon."
|
||||
align="start"
|
||||
>
|
||||
<div class="roles">
|
||||
<Pill><Icon icon="code" size="1.33em" /> Developer</Pill>
|
||||
<Pill><Icon icon="microphone-stage" size="1.33em" /> Speaker</Pill>
|
||||
<Pill><Icon icon="pencil-line" size="1.33em" /> Writer</Pill>
|
||||
</div>
|
||||
</Hero>
|
||||
|
||||
<img
|
||||
alt="Jeanine White smiling in a red plaid shirt and tortoise shell glasses"
|
||||
width="480"
|
||||
height="620"
|
||||
src="/assets/portrait.jpg"
|
||||
/>
|
||||
</header>
|
||||
|
||||
<Skills />
|
||||
</div>
|
||||
|
||||
<main class="wrapper stack gap-20 lg:gap-48">
|
||||
<section class="section with-background with-cta">
|
||||
<header class="section-header stack gap-2 lg:gap-4">
|
||||
<h3>Selected Work</h3>
|
||||
<p>Take a look below at some of my featured work for clients from the past few years.</p>
|
||||
</header>
|
||||
|
||||
<div class="gallery">
|
||||
<Grid variant="offset">
|
||||
{
|
||||
projects.map((project) => (
|
||||
<li>
|
||||
<PortfolioPreview project={project} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</Grid>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h3 class="sectionTitle">About me</h3>
|
||||
<p class="bio">
|
||||
<span>Hello!</span> I’m Jeanine, and this is my website. It was made using{' '}
|
||||
<a href="https://github.com/withastro/astro" target="_blank" rel="nofollow"> Astro</a>,
|
||||
a new way to build static sites. This is just an example template for you to modify.
|
||||
</p>
|
||||
|
||||
<div class="cta">
|
||||
<CallToAction href="/work/">
|
||||
View All
|
||||
<Icon icon="arrow-right" size="1.2em" />
|
||||
</CallToAction>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section with-background bg-variant">
|
||||
<header class="section-header stack gap-2 lg:gap-4">
|
||||
<h3>Mentions</h3>
|
||||
<p>
|
||||
<a href="/about">Read more</a>
|
||||
I have been fortunate enough to recieve praise for my work in several publications. Take
|
||||
a look below to learn more.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="gallery">
|
||||
<Grid variant="small">
|
||||
{
|
||||
['Medium', 'BuzzFeed', 'The Next Web', 'awwwards.', 'TechCrunch'].map((brand) => (
|
||||
<li class="mention-card">
|
||||
<p>{brand}</p>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</Grid>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<ContactCTA />
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
.hero {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.roles {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero img {
|
||||
aspect-ratio: 5 / 4;
|
||||
object-fit: cover;
|
||||
object-position: top;
|
||||
border-radius: 1.5rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.hero {
|
||||
display: grid;
|
||||
grid-template-columns: 6fr 4fr;
|
||||
padding-inline: 2.5rem;
|
||||
gap: 3.75rem;
|
||||
}
|
||||
|
||||
.roles {
|
||||
margin-top: 0.5rem;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.hero img {
|
||||
aspect-ratio: 3 / 4;
|
||||
border-radius: 4.5rem;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
/* ====================================================== */
|
||||
|
||||
.section {
|
||||
display: grid;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.with-background {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.with-background::before {
|
||||
--hero-bg: var(--bg-image-subtle-2);
|
||||
|
||||
content: '';
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
left: 50%;
|
||||
width: 100vw;
|
||||
aspect-ratio: calc(2.25 / var(--bg-scale));
|
||||
top: 0;
|
||||
transform: translateY(-75%) translateX(-50%);
|
||||
background: url('/assets/backgrounds/noise.png') top center/220px repeat,
|
||||
var(--hero-bg) center center / var(--bg-gradient-size) no-repeat, var(--gray-999);
|
||||
background-blend-mode: overlay, normal, normal, normal;
|
||||
mix-blend-mode: var(--bg-blend-mode);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.with-background.bg-variant::before {
|
||||
--hero-bg: var(--bg-image-subtle-1);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
justify-self: center;
|
||||
text-align: center;
|
||||
max-width: 50ch;
|
||||
font-size: var(--text-md);
|
||||
color: var(--gray-300);
|
||||
}
|
||||
|
||||
.section-header h3 {
|
||||
font-size: var(--text-2xl);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.section {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-areas: 'header header header header' 'gallery gallery gallery gallery';
|
||||
gap: 5rem;
|
||||
}
|
||||
|
||||
.section.with-cta {
|
||||
grid-template-areas: 'header header header cta' 'gallery gallery gallery gallery';
|
||||
}
|
||||
|
||||
.section-header {
|
||||
grid-area: header;
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
|
||||
.section-header h3 {
|
||||
font-size: var(--text-4xl);
|
||||
}
|
||||
|
||||
.with-cta .section-header {
|
||||
justify-self: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.gallery {
|
||||
grid-area: gallery;
|
||||
}
|
||||
|
||||
.cta {
|
||||
grid-area: cta;
|
||||
}
|
||||
}
|
||||
|
||||
/* ====================================================== */
|
||||
|
||||
.mention-card {
|
||||
display: flex;
|
||||
height: 7rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
border: 1px solid var(--gray-800);
|
||||
border-radius: 1.5rem;
|
||||
color: var(--gray-300);
|
||||
background: var(--gradient-subtle);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.mention-card {
|
||||
border-radius: 1.5rem;
|
||||
height: 9.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
---
|
||||
layout: ../../layouts/project.astro
|
||||
title: Mars Rover
|
||||
client: Self
|
||||
publishDate: 2020-03-02 00:00:00
|
||||
img: https://images.unsplash.com/photo-1547234935-80c7145ec969?fit=crop&w=1400&h=700&q=75
|
||||
description: |
|
||||
We built an unofficial Mars Rover Landing site in celebration of NASA’s Perseverance Rover.
|
||||
tags:
|
||||
- design
|
||||
- dev
|
||||
- branding
|
||||
---
|
||||
|
||||
Rubber cheese mascarpone cut the cheese. Jarlsberg parmesan cheesy grin cream cheese port-salut stinking bishop ricotta brie. Roquefort when the cheese comes out everybody's happy goat cheese triangles stilton cheese and biscuits goat babybel. Bocconcini roquefort queso danish fontina pecorino.
|
||||
|
||||
Smelly cheese stinking bishop roquefort. Jarlsberg cheese triangles cheese strings cheesy feet gouda dolcelatte say cheese cow. Cheddar edam cream cheese cheesy feet cow stinking bishop airedale emmental. Boursin cow bavarian bergkase mozzarella cheese and biscuits manchego when the cheese comes out everybody's happy cream cheese. Cheese on toast st. agur blue cheese croque monsieur halloumi.
|
||||
|
||||
Fromage frais jarlsberg st. agur blue cheese. Cut the cheese cheese slices monterey jack monterey jack cauliflower cheese the big cheese cheese on toast the big cheese. Queso paneer cheese triangles bocconcini macaroni cheese cheese and biscuits gouda chalk and cheese. Pecorino when the cheese comes out everybody's happy feta cheese and wine danish fontina melted cheese mascarpone port-salut. When the cheese comes out everybody's happy pecorino cottage cheese.
|
||||
|
||||
Caerphilly parmesan manchego. Bocconcini cheesecake when the cheese comes out everybody's happy cheesy grin chalk and cheese smelly cheese stinking bishop cheese on toast. Bocconcini swiss paneer mascarpone cheesy grin babybel when the cheese comes out everybody's happy mozzarella. Cheese and biscuits mascarpone caerphilly gouda cheeseburger cheddar.
|
||||
|
||||
Cheese and biscuits cheesy grin roquefort. Ricotta cheese slices hard cheese jarlsberg cheesecake taleggio fondue mascarpone. Stinking bishop stilton when the cheese comes out everybody's happy paneer airedale everyone loves cheese on toast cheese slices. Ricotta cut the cheese cheese triangles babybel cream cheese ricotta.
|
|
@ -1,23 +0,0 @@
|
|||
---
|
||||
layout: ../../../layouts/project.astro
|
||||
title: Lunar Eclipse
|
||||
client: Self
|
||||
publishDate: 2020-03-04 00:00:00
|
||||
img: https://images.unsplash.com/photo-1548391350-1a529f6ea42d?fit=crop&w=1400&h=700&q=75
|
||||
description: |
|
||||
We took some cool pictures of the moon and made a website about it.
|
||||
tags:
|
||||
- design
|
||||
- dev
|
||||
- branding
|
||||
---
|
||||
|
||||
Rubber cheese mascarpone cut the cheese. Jarlsberg parmesan cheesy grin cream cheese port-salut stinking bishop ricotta brie. Roquefort when the cheese comes out everybody's happy goat cheese triangles stilton cheese and biscuits goat babybel. Bocconcini roquefort queso danish fontina pecorino.
|
||||
|
||||
Smelly cheese stinking bishop roquefort. Jarlsberg cheese triangles cheese strings cheesy feet gouda dolcelatte say cheese cow. Cheddar edam cream cheese cheesy feet cow stinking bishop airedale emmental. Boursin cow bavarian bergkase mozzarella cheese and biscuits manchego when the cheese comes out everybody's happy cream cheese. Cheese on toast st. agur blue cheese croque monsieur halloumi.
|
||||
|
||||
Fromage frais jarlsberg st. agur blue cheese. Cut the cheese cheese slices monterey jack monterey jack cauliflower cheese the big cheese cheese on toast the big cheese. Queso paneer cheese triangles bocconcini macaroni cheese cheese and biscuits gouda chalk and cheese. Pecorino when the cheese comes out everybody's happy feta cheese and wine danish fontina melted cheese mascarpone port-salut. When the cheese comes out everybody's happy pecorino cottage cheese.
|
||||
|
||||
Caerphilly parmesan manchego. Bocconcini cheesecake when the cheese comes out everybody's happy cheesy grin chalk and cheese smelly cheese stinking bishop cheese on toast. Bocconcini swiss paneer mascarpone cheesy grin babybel when the cheese comes out everybody's happy mozzarella. Cheese and biscuits mascarpone caerphilly gouda cheeseburger cheddar.
|
||||
|
||||
Cheese and biscuits cheesy grin roquefort. Ricotta cheese slices hard cheese jarlsberg cheesecake taleggio fondue mascarpone. Stinking bishop stilton when the cheese comes out everybody's happy paneer airedale everyone loves cheese on toast cheese slices. Ricotta cut the cheese cheese triangles babybel cream cheese ricotta.
|
|
@ -1,43 +0,0 @@
|
|||
---
|
||||
import MainHead from '../components/MainHead.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import Nav from '../components/Nav.astro';
|
||||
import PortfolioPreview from '../components/PortfolioPreview.astro';
|
||||
import type { Project } from '../types';
|
||||
|
||||
const projects = (await Astro.glob<Project>('./project/**/*.md'))
|
||||
.filter(({ frontmatter }) => !!frontmatter.publishDate)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
new Date(b.frontmatter.publishDate).valueOf() - new Date(a.frontmatter.publishDate).valueOf()
|
||||
);
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<MainHead
|
||||
title="All Projects | Jeanine White"
|
||||
description="Learn about Jeanine White's most recent projects"
|
||||
/>
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-gap: 3rem;
|
||||
}
|
||||
.title {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<Nav />
|
||||
<div class="wrapper">
|
||||
<h1 class="title">All Projects</h1>
|
||||
<div class="grid">
|
||||
{projects.map((project) => <PortfolioPreview project={project} />)}
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
39
examples/portfolio/src/pages/work.astro
Normal file
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
|
||||
import ContactCTA from '../components/ContactCTA.astro';
|
||||
import PortfolioPreview from '../components/PortfolioPreview.astro';
|
||||
import Hero from '../components/Hero.astro';
|
||||
import Grid from '../components/Grid.astro';
|
||||
|
||||
const projects = (await getCollection('work')).sort(
|
||||
(a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf()
|
||||
);
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title="My Work | Jeanine White"
|
||||
description="Learn about Jeanine White's most recent projects"
|
||||
>
|
||||
<div class="stack gap-20">
|
||||
<main class="wrapper stack gap-8">
|
||||
<Hero
|
||||
title="My Work"
|
||||
tagline="See my most recent projects below to get an idea of my past experience."
|
||||
align="start"
|
||||
/>
|
||||
<Grid variant="offset">
|
||||
{
|
||||
projects.map((project) => (
|
||||
<li>
|
||||
<PortfolioPreview project={project} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</Grid>
|
||||
</main>
|
||||
<ContactCTA />
|
||||
</div>
|
||||
</BaseLayout>
|
151
examples/portfolio/src/pages/work/[...slug].astro
Normal file
|
@ -0,0 +1,151 @@
|
|||
---
|
||||
import { CollectionEntry, getCollection } from 'astro:content';
|
||||
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
|
||||
import ContactCTA from '../../components/ContactCTA.astro';
|
||||
import Hero from '../../components/Hero.astro';
|
||||
import Icon from '../../components/Icon.astro';
|
||||
import Pill from '../../components/Pill.astro';
|
||||
|
||||
interface Props {
|
||||
entry: CollectionEntry<'work'>;
|
||||
}
|
||||
|
||||
// This is a dynamic route that generates a page for every Markdown file in src/content/
|
||||
// Read more about dynamic routes and this `getStaticPaths` function in the Astro docs:
|
||||
// https://docs.astro.build/en/core-concepts/routing/#dynamic-routes
|
||||
export async function getStaticPaths() {
|
||||
const work = await getCollection('work');
|
||||
return work.map((entry) => ({
|
||||
params: { slug: entry.slug },
|
||||
props: { entry },
|
||||
}));
|
||||
}
|
||||
|
||||
const { entry } = Astro.props;
|
||||
const { Content } = await entry.render();
|
||||
---
|
||||
|
||||
<BaseLayout title={entry.data.title} description={entry.data.description}>
|
||||
<div class="stack gap-20">
|
||||
<div class="stack gap-15">
|
||||
<header>
|
||||
<div class="wrapper stack gap-2">
|
||||
<a class="back-link" href="/work/"><Icon icon="arrow-left" /> Work</a>
|
||||
<Hero title={entry.data.title} align="start">
|
||||
<div class="details">
|
||||
<div class="tags">
|
||||
{entry.data.tags.map((t) => <Pill>{t}</Pill>)}
|
||||
</div>
|
||||
<p class="description">{entry.data.description}</p>
|
||||
</div>
|
||||
</Hero>
|
||||
</div>
|
||||
</header>
|
||||
<main class="wrapper">
|
||||
<div class="stack gap-10 content">
|
||||
{entry.data.img && <img src={entry.data.img} alt={entry.data.img_alt || ''} />}
|
||||
<div class="content">
|
||||
<Content />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<ContactCTA />
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
header {
|
||||
padding-bottom: 2.5rem;
|
||||
border-bottom: 1px solid var(--gray-800);
|
||||
}
|
||||
|
||||
.back-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.5rem;
|
||||
gap: 1.5rem;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: var(--text-lg);
|
||||
max-width: 54ch;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 65ch;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.content > :global(* + *) {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.content :global(h1),
|
||||
.content :global(h2),
|
||||
.content :global(h3),
|
||||
.content :global(h4),
|
||||
.content :global(h5) {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.content :global(img) {
|
||||
border-radius: 1.5rem;
|
||||
box-shadow: var(--shadow-sm);
|
||||
background: var(--gradient-subtle);
|
||||
border: 1px solid var(--gray-800);
|
||||
}
|
||||
|
||||
.content :global(blockquote) {
|
||||
font-size: var(--text-lg);
|
||||
font-family: var(--font-brand);
|
||||
font-weight: 600;
|
||||
line-height: 1.1;
|
||||
padding-inline-start: 1.5rem;
|
||||
border-inline-start: 0.25rem solid var(--accent-dark);
|
||||
color: var(--gray-0);
|
||||
}
|
||||
|
||||
.back-link,
|
||||
.content :global(a) {
|
||||
text-decoration: 1px solid underline transparent;
|
||||
text-underline-offset: 0.25em;
|
||||
transition: text-decoration-color var(--theme-transition);
|
||||
}
|
||||
|
||||
.back-link:hover,
|
||||
.back-link:focus,
|
||||
.content :global(a:hover),
|
||||
.content :global(a:focus) {
|
||||
text-decoration-color: currentColor;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.back-link {
|
||||
display: block;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.details {
|
||||
flex-direction: row;
|
||||
gap: 2.5rem;
|
||||
}
|
||||
|
||||
.content :global(blockquote) {
|
||||
font-size: var(--text-2xl);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,67 +1,139 @@
|
|||
/* Global variables */
|
||||
:root {
|
||||
/* Colors */
|
||||
--c-black: #05091e;
|
||||
--c-blue: #46b4ff;
|
||||
--c-gray: #90aab7;
|
||||
--c-green: #9ef2cb;
|
||||
--c-pink: #ffb8d9;
|
||||
--c-orange: #ffb7a3;
|
||||
--c-yellow: #ffdace;
|
||||
--c-white: #fff;
|
||||
--gray-0: #090b11;
|
||||
--gray-50: #141925;
|
||||
--gray-100: #283044;
|
||||
--gray-200: #3d4663;
|
||||
--gray-300: #505d84;
|
||||
--gray-400: #6474a2;
|
||||
--gray-500: #8490b5;
|
||||
--gray-600: #a3acc8;
|
||||
--gray-700: #c3cadb;
|
||||
--gray-800: #e3e6ee;
|
||||
--gray-900: #f3f4f7;
|
||||
--gray-999-basis: 0, 0%, 100%;
|
||||
--gray-999_40: hsla(var(--gray-999-basis), 0.4);
|
||||
--gray-999: #ffffff;
|
||||
|
||||
--accent-light: #c561f6;
|
||||
--accent-regular: #7611a6;
|
||||
--accent-dark: #1c0056;
|
||||
--accent-overlay: hsla(280, 89%, 67%, 0.33);
|
||||
--accent-subtle-overlay: var(--accent-overlay);
|
||||
--accent-text-over: var(--gray-999);
|
||||
|
||||
--link-color: var(--accent-regular);
|
||||
|
||||
/* Gradients */
|
||||
--gradient-stop-1: var(--accent-light);
|
||||
--gradient-stop-2: var(--accent-regular);
|
||||
--gradient-stop-3: var(--accent-dark);
|
||||
--gradient-subtle: linear-gradient(150deg, var(--gray-900) 19%, var(--gray-999) 150%);
|
||||
--gradient-accent: linear-gradient(
|
||||
150deg,
|
||||
var(--gradient-stop-1),
|
||||
var(--gradient-stop-2),
|
||||
var(--gradient-stop-3)
|
||||
);
|
||||
--gradient-accent-orange: linear-gradient(
|
||||
150deg,
|
||||
#ca7879,
|
||||
var(--accent-regular),
|
||||
var(--accent-dark)
|
||||
);
|
||||
--gradient-stroke: linear-gradient(180deg, var(--gray-900), var(--gray-700));
|
||||
|
||||
/* Shadows */
|
||||
--shadow-sm: 0px 6px 3px rgba(9, 11, 17, 0.01), 0px 4px 2px rgba(9, 11, 17, 0.01),
|
||||
0px 2px 2px rgba(9, 11, 17, 0.02), 0px 0px 1px rgba(9, 11, 17, 0.03);
|
||||
--shadow-md: 0px 28px 11px rgba(9, 11, 17, 0.01), 0px 16px 10px rgba(9, 11, 17, 0.03),
|
||||
0px 7px 7px rgba(9, 11, 17, 0.05), 0px 2px 4px rgba(9, 11, 17, 0.06);
|
||||
--shadow-lg: 0px 62px 25px rgba(9, 11, 17, 0.01), 0px 35px 21px rgba(9, 11, 17, 0.05),
|
||||
0px 16px 16px rgba(9, 11, 17, 0.1), 0px 4px 9px rgba(9, 11, 17, 0.12);
|
||||
|
||||
/* Text Sizes */
|
||||
--text-sm: 0.875rem;
|
||||
--text-base: 1rem;
|
||||
--text-md: 1.125rem;
|
||||
--text-lg: 1.25rem;
|
||||
--text-xl: 1.625rem;
|
||||
--text-2xl: 2.125rem;
|
||||
--text-3xl: 2.625rem;
|
||||
--text-4xl: 3.5rem;
|
||||
--text-5xl: 4.5rem;
|
||||
|
||||
/* Fonts */
|
||||
--f-u18: 11.390625em;
|
||||
--f-u17: 9.950627481136905em;
|
||||
--f-u16: 8.692673779389363em;
|
||||
--f-u15: 7.59375em;
|
||||
--f-u14: 6.63375165409127em;
|
||||
--f-u13: 5.795115852926242em;
|
||||
--f-u12: 5.0625em;
|
||||
--f-u11: 4.422501102727513em;
|
||||
--f-u10: 3.8634105686174953em;
|
||||
--f-u9: 3.375em;
|
||||
--f-u8: 2.9483340684850083em;
|
||||
--f-u7: 2.575607045744997em;
|
||||
--f-u6: 2.25em;
|
||||
--f-u5: 1.9655560456566725em;
|
||||
--f-u4: 1.7170713638299977em;
|
||||
--f-u3: 1.5em;
|
||||
--f-u2: 1.3103706971044482em;
|
||||
--f-u1: 1.1447142425533319em;
|
||||
--f-d1: 0.8735804647362989em;
|
||||
--f-d2: 0.7631428283688879em;
|
||||
--f-d3: 0.6666666666666666em;
|
||||
--f-d4: 0.5823869764908659em;
|
||||
--f-d5: 0.5087618855792586em;
|
||||
--f-d6: 0.4444444444444444em;
|
||||
--f-d7: 0.3882579843272439em;
|
||||
--f-d8: 0.3391745903861724em;
|
||||
--f-d9: 0.2962962962962963em;
|
||||
--f-d10: 0.2588386562181626em;
|
||||
--f-d11: 0.22611639359078162em;
|
||||
--f-d12: 0.19753086419753085em;
|
||||
--f-d13: 0.17255910414544176em;
|
||||
--f-d14: 0.15074426239385438em;
|
||||
--f-d15: 0.13168724279835392em;
|
||||
--f-d16: 0.11503940276362785em;
|
||||
--f-d17: 0.10049617492923625em;
|
||||
--f-d18: 0.0877914951989026em;
|
||||
--font-system: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
--font-body: 'Public Sans', var(--font-system);
|
||||
--font-brand: Rubik, var(--font-system);
|
||||
|
||||
--t-fg: var(--c-black);
|
||||
--t-bg: var(--c-white);
|
||||
--t-subdue: var(--c-gray);
|
||||
--t-active: var(--c-blue);
|
||||
/* Transitions */
|
||||
--theme-transition: 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
:root.theme-dark {
|
||||
--gray-0: #ffffff;
|
||||
--gray-50: #f3f4f7;
|
||||
--gray-100: #e3e6ee;
|
||||
--gray-200: #c3cadb;
|
||||
--gray-300: #a3acc8;
|
||||
--gray-400: #8490b5;
|
||||
--gray-500: #6474a2;
|
||||
--gray-600: #505d84;
|
||||
--gray-700: #3d4663;
|
||||
--gray-800: #283044;
|
||||
--gray-900: #141925;
|
||||
--gray-999-basis: 225, 31%, 5%;
|
||||
--gray-999: #090b11;
|
||||
|
||||
--accent-light: #1c0056;
|
||||
--accent-regular: #7611a6;
|
||||
--accent-dark: #c561f6;
|
||||
--accent-overlay: hsla(280, 89%, 67%, 0.33);
|
||||
--accent-subtle-overlay: hsla(281, 81%, 36%, 0.33);
|
||||
--accent-text-over: var(--gray-0);
|
||||
|
||||
--link-color: var(--accent-dark);
|
||||
|
||||
--gradient-stop-1: #4c11c6;
|
||||
--gradient-subtle: linear-gradient(150deg, var(--gray-900) 19%, var(--gray-999) 81%);
|
||||
--gradient-accent-orange: linear-gradient(
|
||||
150deg,
|
||||
#ca7879,
|
||||
var(--accent-regular),
|
||||
var(--accent-light)
|
||||
);
|
||||
--gradient-stroke: linear-gradient(180deg, var(--gray-600), var(--gray-800));
|
||||
|
||||
--shadow-sm: 0px 6px 3px rgba(255, 255, 255, 0.01), 0px 4px 2px rgba(255, 255, 255, 0.01),
|
||||
0px 2px 2px rgba(255, 255, 255, 0.02), 0px 0px 1px rgba(255, 255, 255, 0.03);
|
||||
--shadow-md: 0px 28px 11px rgba(255, 255, 255, 0.01), 0px 16px 10px rgba(255, 255, 255, 0.03),
|
||||
0px 7px 7px rgba(255, 255, 255, 0.05), 0px 2px 4px rgba(255, 255, 255, 0.06);
|
||||
--shadow-lg: 0px 62px 25px rgba(255, 255, 255, 0.01), 0px 35px 21px rgba(255, 255, 255, 0.05),
|
||||
0px 16px 16px rgba(255, 255, 255, 0.1), 0px 4px 9px rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
color: var(--t-fg);
|
||||
font-family: 'Inter', 'system-ui', sans-serif;
|
||||
background-color: var(--gray-999);
|
||||
color: var(--gray-200);
|
||||
font-family: var(--font-body);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: content-box;
|
||||
*,
|
||||
*::after,
|
||||
*::before {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
|
@ -70,25 +142,114 @@ img {
|
|||
}
|
||||
|
||||
a {
|
||||
color: var(--t-active);
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5 {
|
||||
line-height: 1.1;
|
||||
font-family: var(--font-brand);
|
||||
font-weight: 600;
|
||||
color: var(--gray-100);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: var(--f-u8);
|
||||
font-size: var(--text-5xl);
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
border: 3px solid currentColor;
|
||||
padding: 0.5em 1em;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
h2 {
|
||||
font-size: var(--text-4xl);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: var(--text-3xl);
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: var(--text-2xl);
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: var(--text-xl);
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
max-width: 80em;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
width: 100%;
|
||||
max-width: 83rem;
|
||||
margin-inline: auto;
|
||||
padding-inline: 1.5rem;
|
||||
}
|
||||
|
||||
.stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.gap-4 {
|
||||
gap: 1rem;
|
||||
}
|
||||
.gap-8 {
|
||||
gap: 2rem;
|
||||
}
|
||||
.gap-10 {
|
||||
gap: 2.5rem;
|
||||
}
|
||||
.gap-15 {
|
||||
gap: 3.75rem;
|
||||
}
|
||||
.gap-20 {
|
||||
gap: 5rem;
|
||||
}
|
||||
.gap-30 {
|
||||
gap: 7.5rem;
|
||||
}
|
||||
.gap-48 {
|
||||
gap: 12rem;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.lg\:gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.lg\:gap-4 {
|
||||
gap: 1rem;
|
||||
}
|
||||
.lg\:gap-8 {
|
||||
gap: 2rem;
|
||||
}
|
||||
.lg\:gap-10 {
|
||||
gap: 2.5rem;
|
||||
}
|
||||
.lg\:gap-15 {
|
||||
gap: 3.75rem;
|
||||
}
|
||||
.lg\:gap-20 {
|
||||
gap: 5rem;
|
||||
}
|
||||
.lg\:gap-30 {
|
||||
gap: 7.5rem;
|
||||
}
|
||||
.lg\:gap-48 {
|
||||
gap: 12rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
export interface Project {
|
||||
title: string;
|
||||
client: string;
|
||||
description: string;
|
||||
publishDate: string;
|
||||
tags: string[];
|
||||
img: string;
|
||||
}
|