run prettier

This commit is contained in:
Chuck Dries 2021-07-12 18:54:08 -07:00
parent 5df4c2baf4
commit d8655904fa
31 changed files with 1183 additions and 917 deletions

View File

@ -2,24 +2,24 @@ module.exports = {
globals: { globals: {
__PATH_PREFIX__: true, __PATH_PREFIX__: true,
}, },
'parser': 'babel-eslint', // uses babel-eslint transforms parser: "babel-eslint", // uses babel-eslint transforms
'settings': { settings: {
'react': { react: {
'version': 'detect', // detect react version version: "detect", // detect react version
}, },
}, },
'env': { env: {
'node': true, // defines things like process.env when generating through node node: true, // defines things like process.env when generating through node
'browser': true browser: true,
}, },
'extends': [ extends: [
'eslint:recommended', // use recommended configs "eslint:recommended", // use recommended configs
'plugin:react/recommended', "plugin:react/recommended",
'plugin:react-hooks/recommended', "plugin:react-hooks/recommended",
], ],
'rules': { rules: {
'react/prop-types': 0, "react/prop-types": 0,
'no-unused-vars': 1, "no-unused-vars": 1,
'react/jsx-sort-props': 1, "react/jsx-sort-props": 1,
}, },
}; };

11
.vscode/launch.json vendored
View File

@ -1,6 +1,7 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [{ "configurations": [
{
"name": "Launch Chrome", "name": "Launch Chrome",
"type": "chrome", "type": "chrome",
"request": "launch", "request": "launch",
@ -18,9 +19,7 @@
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": null, "preLaunchTask": null,
"runtimeExecutable": null, "runtimeExecutable": null,
"runtimeArgs": [ "runtimeArgs": ["--nolazy"],
"--nolazy"
],
"env": { "env": {
"NODE_ENV": "development" "NODE_ENV": "development"
}, },
@ -38,9 +37,7 @@
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": null, "preLaunchTask": null,
"runtimeExecutable": null, "runtimeExecutable": null,
"runtimeArgs": [ "runtimeArgs": ["--nolazy"],
"--nolazy"
],
"env": { "env": {
"NODE_ENV": "development" "NODE_ENV": "development"
}, },

View File

@ -1,10 +1,12 @@
- Gallery - Gallery
- [x] Custom image metadata - [x] Custom image metadata
- [ ] add metadata to image files - [ ] add metadata to image files
- [ ] image details page (modal route?) - [ ] image details page (modal route?)
- [ ] gallery page - [ ] gallery page
- Homepage - Homepage
- [ ] homepage basic layout - [ ] homepage basic layout
- [ ] homepage aesthetics - [ ] homepage aesthetics
- [ ] homepage to gallery page transition - [ ] homepage to gallery page transition
@ -14,6 +16,7 @@
- [ ] About page? - [ ] About page?
- Portfolio/Projects page - Portfolio/Projects page
- basic layout - basic layout
- source data - source data
- aesthetics - aesthetics

View File

@ -1,17 +1,17 @@
import './src/styles/global.css'; import "./src/styles/global.css";
import posthog from 'posthog-js'; import posthog from "posthog-js";
const env = process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || 'development'; const env =
if (env === 'production') { process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || "development";
posthog.init('HR8Gte105aCHNx2BqhL1XkbvH9kzKGptxjkbhuTj6Ek', { api_host: 'https://posthog.chuckdries.com' }); if (env === "production") {
posthog.init("HR8Gte105aCHNx2BqhL1XkbvH9kzKGptxjkbhuTj6Ek", {
api_host: "https://posthog.chuckdries.com",
});
} }
export const onRouteUpdate = function () { export const onRouteUpdate = function () {
if ( if (env === "production" && typeof window.plausible === "object") {
env === 'production' && window.plausible("pageview");
typeof window.plausible === 'object' posthog.capture("$pageview");
) {
window.plausible('pageview');
posthog.capture('$pageview');
} }
}; };
// import * as React from 'react'; // import * as React from 'react';

View File

@ -1,56 +1,56 @@
module.exports = { module.exports = {
siteMetadata: { siteMetadata: {
title: 'Chuck Dries', title: "Chuck Dries",
siteUrl: 'https://chuckdries.com', siteUrl: "https://chuckdries.com",
}, },
plugins: [ plugins: [
'gatsby-plugin-sass', "gatsby-plugin-sass",
'gatsby-plugin-image', "gatsby-plugin-image",
'gatsby-plugin-react-helmet', "gatsby-plugin-react-helmet",
{ {
resolve: 'gatsby-plugin-manifest', resolve: "gatsby-plugin-manifest",
options: { options: {
icon: 'src/images/glasses-outline.svg', icon: "src/images/glasses-outline.svg",
}, },
}, },
'gatsby-plugin-mdx', "gatsby-plugin-mdx",
'gatsby-plugin-sharp', "gatsby-plugin-sharp",
'gatsby-transformer-sharp', "gatsby-transformer-sharp",
'gatsby-plugin-postcss', "gatsby-plugin-postcss",
{ {
resolve: 'gatsby-source-filesystem', resolve: "gatsby-source-filesystem",
options: { options: {
name: 'images', name: "images",
path: './src/images/', path: "./src/images/",
}, },
__key: 'images', __key: "images",
}, },
{ {
resolve: 'gatsby-source-filesystem', resolve: "gatsby-source-filesystem",
options: { options: {
name: 'gallery', name: "gallery",
path: './data/gallery/', path: "./data/gallery/",
}, },
__key: 'gallery', __key: "gallery",
}, },
{ {
resolve: 'gatsby-source-filesystem', resolve: "gatsby-source-filesystem",
options: { options: {
name: 'pages', name: "pages",
path: './src/pages/', path: "./src/pages/",
}, },
__key: 'pages', __key: "pages",
}, },
{ {
resolve: 'gatsby-plugin-eslint', resolve: "gatsby-plugin-eslint",
options: { options: {
stages: ['develop'], stages: ["develop"],
extensions: ['js', 'jsx'], extensions: ["js", "jsx"],
exclude: ['node_modules', '.cache', 'public'], exclude: ["node_modules", ".cache", "public"],
// Any eslint-webpack-plugin options below // Any eslint-webpack-plugin options below
}, },
}, },
'gatsby-plugin-preval', "gatsby-plugin-preval",
'gatsby-plugin-robots-txt', "gatsby-plugin-robots-txt",
], ],
}; };

View File

@ -1,12 +1,12 @@
const fs = require('fs'); const fs = require("fs");
const util = require('util'); const util = require("util");
const path = require('path'); const path = require("path");
const { read } = require('fast-exif'); const { read } = require("fast-exif");
const iptc = require('node-iptc'); const iptc = require("node-iptc");
const Vibrant = require('node-vibrant'); const Vibrant = require("node-vibrant");
const chroma = require('chroma-js'); const chroma = require("chroma-js");
const chalk = require('chalk'); const chalk = require("chalk");
const R = require('ramda'); const R = require("ramda");
const readFile = util.promisify(fs.readFile); const readFile = util.promisify(fs.readFile);
@ -15,9 +15,11 @@ const badContrast = (color1, color2) => chroma.contrast(color1, color2) < 4.5;
const logColorsWithContrast = (color1, color2, text) => { const logColorsWithContrast = (color1, color2, text) => {
const c1hex = color1.hex(); const c1hex = color1.hex();
const c2hex = color2.hex(); const c2hex = color2.hex();
console.log(chalk.hex(c1hex).bgHex(c2hex)( console.log(
chalk.hex(c1hex).bgHex(c2hex)(
`${text} ${c1hex}/${c2hex} ${chroma.contrast(color1, color2)}` `${text} ${c1hex}/${c2hex} ${chroma.contrast(color1, color2)}`
)); )
);
}; };
function processColors(vibrantData, imagePath) { function processColors(vibrantData, imagePath) {
@ -29,7 +31,10 @@ function processColors(vibrantData, imagePath) {
let LightMuted = chroma(vibrantData.LightMuted.getRgb()); let LightMuted = chroma(vibrantData.LightMuted.getRgb());
// first pass - darken bg and lighten relevant fg colors // first pass - darken bg and lighten relevant fg colors
if (badContrast(DarkVibrant, Vibrant) || badContrast(DarkVibrant, LightMuted)) { if (
badContrast(DarkVibrant, Vibrant) ||
badContrast(DarkVibrant, LightMuted)
) {
DarkVibrant = DarkVibrant.darken(); DarkVibrant = DarkVibrant.darken();
if (badContrast(DarkVibrant, Vibrant)) { if (badContrast(DarkVibrant, Vibrant)) {
Vibrant = Vibrant.brighten(); Vibrant = Vibrant.brighten();
@ -39,7 +44,10 @@ function processColors(vibrantData, imagePath) {
} }
} }
// second pass - first doesn't always do enough // second pass - first doesn't always do enough
if (badContrast(DarkVibrant, Vibrant) || badContrast(DarkVibrant, LightMuted)) { if (
badContrast(DarkVibrant, Vibrant) ||
badContrast(DarkVibrant, LightMuted)
) {
// DarkVibrant = DarkVibrant.darken(); // DarkVibrant = DarkVibrant.darken();
if (badContrast(DarkVibrant, Vibrant)) { if (badContrast(DarkVibrant, Vibrant)) {
Vibrant = Vibrant.brighten(2); Vibrant = Vibrant.brighten(2);
@ -50,15 +58,14 @@ function processColors(vibrantData, imagePath) {
} }
if (badContrast(DarkVibrant, Vibrant)) { if (badContrast(DarkVibrant, Vibrant)) {
console.warn('contrast still too low', imagePath); console.warn("contrast still too low", imagePath);
logColorsWithContrast(Vibrant, DarkVibrant, 'V-DV'); logColorsWithContrast(Vibrant, DarkVibrant, "V-DV");
} }
if (badContrast(DarkVibrant, LightMuted)) { if (badContrast(DarkVibrant, LightMuted)) {
console.warn('contrast still too low', imagePath); console.warn("contrast still too low", imagePath);
logColorsWithContrast(LightMuted, DarkVibrant, 'LM-DV'); logColorsWithContrast(LightMuted, DarkVibrant, "LM-DV");
} }
return { return {
Vibrant: Vibrant.rgb(), Vibrant: Vibrant.rgb(),
DarkVibrant: DarkVibrant.rgb(), DarkVibrant: DarkVibrant.rgb(),
@ -82,25 +89,20 @@ function transformMetaToNodeData(exifData, iptcData, vibrantData, imagePath) {
const gps = { longitude: null, latitude: null }; const gps = { longitude: null, latitude: null };
if (exifData) { if (exifData) {
if ( if (exifData.gps && exifData.gps.GPSLongitude && exifData.gps.GPSLatitude) {
exifData.gps &&
exifData.gps.GPSLongitude &&
exifData.gps.GPSLatitude
) {
gps.longitude = convertDMSToDD( gps.longitude = convertDMSToDD(
exifData.gps.GPSLongitude, exifData.gps.GPSLongitude,
exifData.gps.GPSLongitudeRef === 'E' exifData.gps.GPSLongitudeRef === "E"
); );
gps.latitude = convertDMSToDD( gps.latitude = convertDMSToDD(
exifData.gps.GPSLatitude, exifData.gps.GPSLatitude,
exifData.gps.GPSLatitudeRef === 'N' exifData.gps.GPSLatitudeRef === "N"
); );
} }
} }
const vibrant = vibrantData ? processColors(vibrantData, imagePath) : null; const vibrant = vibrantData ? processColors(vibrantData, imagePath) : null;
return { return {
exif: exifData?.exif, exif: exifData?.exif,
gps, gps,
@ -110,10 +112,9 @@ function transformMetaToNodeData(exifData, iptcData, vibrantData, imagePath) {
}; };
} }
exports.onCreateNode = async function ({ node, actions }) { exports.onCreateNode = async function ({ node, actions }) {
const { createNodeField } = actions; const { createNodeField } = actions;
if (node.internal.type === 'File' && node.sourceInstanceName === 'gallery') { if (node.internal.type === "File" && node.sourceInstanceName === "gallery") {
const file = await readFile(node.absolutePath); const file = await readFile(node.absolutePath);
const iptcData = iptc(file); const iptcData = iptc(file);
const exifData = await read(node.absolutePath); const exifData = await read(node.absolutePath);
@ -123,8 +124,13 @@ exports.onCreateNode = async function ({ node, actions }) {
createNodeField({ createNodeField({
node, node,
name: 'imageMeta', name: "imageMeta",
value: transformMetaToNodeData(exifData, iptcData, vibrantData, node.absolutePath), value: transformMetaToNodeData(
exifData,
iptcData,
vibrantData,
node.absolutePath
),
}); });
} }
}; };
@ -136,13 +142,11 @@ exports.createPages = async ({ graphql, actions, reporter }) => {
// get all images // get all images
const galleryImages = await graphql(` const galleryImages = await graphql(`
{ {
allFile(filter: { allFile(filter: { sourceInstanceName: { eq: "gallery" } }) {
sourceInstanceName: { eq: "gallery" }}
) {
edges { edges {
node { node {
relativePath, relativePath
base, base
fields { fields {
imageMeta { imageMeta {
dateTaken dateTaken
@ -155,25 +159,28 @@ exports.createPages = async ({ graphql, actions, reporter }) => {
`); `);
// Handle errors // Handle errors
if (galleryImages.errors) { if (galleryImages.errors) {
reporter.panicOnBuild('Error while running GraphQL query.'); reporter.panicOnBuild("Error while running GraphQL query.");
return; return;
} }
// Create pages for each markdown file. // Create pages for each markdown file.
const galleryImageTemplate = path.resolve('src/components/GalleryImage/GalleryImage.js'); const galleryImageTemplate = path.resolve(
"src/components/GalleryImage/GalleryImage.js"
);
// const diffDate = (a, b) => // const diffDate = (a, b) =>
// new Date(R.path(['node', 'childImageSharp', 'fields', 'imageMeta', 'dateTaken'], a)).getTime() - new Date(R.path(['node', 'childImageSharp', 'fields', 'imageMeta', 'dateTaken'],b)).getTime(); // new Date(R.path(['node', 'childImageSharp', 'fields', 'imageMeta', 'dateTaken'], a)).getTime() - new Date(R.path(['node', 'childImageSharp', 'fields', 'imageMeta', 'dateTaken'],b)).getTime();
const edges = R.sort(R.descend((edge) => const edges = R.sort(
new Date(R.path(['node', 'fields', 'imageMeta', 'dateTaken'], edge))), R.descend(
galleryImages.data.allFile.edges); (edge) =>
new Date(R.path(["node", "fields", "imageMeta", "dateTaken"], edge))
),
galleryImages.data.allFile.edges
);
edges.forEach(({ node }, index) => { edges.forEach(({ node }, index) => {
const nextImage = index === edges.length - 1 const nextImage =
? null index === edges.length - 1 ? null : edges[index + 1].node.base;
: edges[index + 1].node.base; const prevImage = index === 0 ? null : edges[index - 1].node.base;
const prevImage = index === 0
? null
: edges[index - 1].node.base;
const page = { const page = {
path: `photogallery/${node.base}`, path: `photogallery/${node.base}`,
component: galleryImageTemplate, component: galleryImageTemplate,

View File

@ -1 +1 @@
import './src/styles/global.css'; import "./src/styles/global.css";

View File

@ -1,7 +1,7 @@
module.exports = { module.exports = {
plugins: { plugins: {
tailwindcss: {}, tailwindcss: {},
'postcss-nested': {}, "postcss-nested": {},
autoprefixer: {}, autoprefixer: {},
}, },
}; };

View File

@ -1,4 +1,4 @@
import preval from 'babel-plugin-preval/macro'; import preval from "babel-plugin-preval/macro";
const themeBreakpoints = preval` const themeBreakpoints = preval`
const R = require('ramda') const R = require('ramda')
const resolveConfig = require('tailwindcss/resolveConfig'); const resolveConfig = require('tailwindcss/resolveConfig');

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from "react";
import { graphql, navigate, Link } from 'gatsby'; import { graphql, navigate, Link } from "gatsby";
import { import {
getAspectRatio, getAspectRatio,
getMeta, getMeta,
@ -8,19 +8,21 @@ import {
getVibrant, getVibrant,
getVibrantToHelmetSafeBodyStyle, getVibrantToHelmetSafeBodyStyle,
hasName, hasName,
} from '../../utils'; } from "../../utils";
import { GatsbyImage, getImage } from 'gatsby-plugin-image'; import { GatsbyImage, getImage } from "gatsby-plugin-image";
import { Helmet } from 'react-helmet'; import { Helmet } from "react-helmet";
import classnames from 'classnames'; import classnames from "classnames";
import posthog from 'posthog-js'; import posthog from "posthog-js";
import MetadataItem from './MetadataItem'; import MetadataItem from "./MetadataItem";
const logKeyShortcut = (keyCode) => { const logKeyShortcut = (keyCode) => {
try { try {
// eslint-disable-next-line // eslint-disable-next-line
posthog.capture('[key shortcut]', { keyCode }); posthog.capture("[key shortcut]", { keyCode });
window.plausible('KeyShortcut', {props: { keyCode }}); window.plausible("KeyShortcut", { props: { keyCode } });
} catch (e) {/* do nothing */} } catch (e) {
/* do nothing */
}
}; };
const GalleryImage = ({ data, pageContext }) => { const GalleryImage = ({ data, pageContext }) => {
@ -29,32 +31,31 @@ const GalleryImage = ({ data, pageContext }) => {
React.useEffect(() => { React.useEffect(() => {
const keyListener = (e) => { const keyListener = (e) => {
switch (e.code) { switch (e.code) {
case 'ArrowRight': { case "ArrowRight": {
logKeyShortcut(e.code); logKeyShortcut(e.code);
if (pageContext.nextImage) { if (pageContext.nextImage) {
navigate(`/photogallery/${pageContext.nextImage}/`); navigate(`/photogallery/${pageContext.nextImage}/`);
} }
return; return;
} }
case 'ArrowLeft': { case "ArrowLeft": {
logKeyShortcut(e.code); logKeyShortcut(e.code);
if (pageContext.prevImage) { if (pageContext.prevImage) {
navigate(`/photogallery/${pageContext.prevImage}/`); navigate(`/photogallery/${pageContext.prevImage}/`);
} }
return; return;
} }
case 'Escape': case "Escape":
case 'KeyG': { case "KeyG": {
logKeyShortcut(e.code); logKeyShortcut(e.code);
navigate('/photogallery/'); navigate("/photogallery/");
} }
} }
}; };
document.addEventListener('keydown', keyListener); document.addEventListener("keydown", keyListener);
return () => { return () => {
document.removeEventListener('keydown', keyListener); document.removeEventListener("keydown", keyListener);
}; };
}, [pageContext]); }, [pageContext]);
@ -62,15 +63,24 @@ const GalleryImage = ({ data, pageContext }) => {
const meta = getMeta(image); const meta = getMeta(image);
let locationString; let locationString;
if (meta.iptc.city || meta.iptc.province_or_state) { if (meta.iptc.city || meta.iptc.province_or_state) {
const location = [meta.iptc.city, meta.iptc.province_or_state].filter(Boolean); const location = [meta.iptc.city, meta.iptc.province_or_state].filter(
locationString = location.join(', '); Boolean
);
locationString = location.join(", ");
} }
const vibrant = getVibrant(image, true); const vibrant = getVibrant(image, true);
const orientationClasses = ar > 1 ? 'flex-col mx-auto' : 'portrait:mx-auto landscape:mx-5 landscape:flex-row-reverse portrait:flex-col'; const orientationClasses =
const shutterSpeed = React.useMemo(() => getShutterFractionFromExposureTime(meta.exif.ExposureTime || 0), [meta]); ar > 1
? "flex-col mx-auto"
: "portrait:mx-auto landscape:mx-5 landscape:flex-row-reverse portrait:flex-col";
const shutterSpeed = React.useMemo(
() => getShutterFractionFromExposureTime(meta.exif.ExposureTime || 0),
[meta]
);
const dateTaken = React.useMemo(() => new Date(meta.dateTaken), [meta]); const dateTaken = React.useMemo(() => new Date(meta.dateTaken), [meta]);
return (<> return (
<>
<Helmet> <Helmet>
<title>{name} - Gallery | Chuck Dries</title> <title>{name} - Gallery | Chuck Dries</title>
<body <body
@ -84,25 +94,39 @@ const GalleryImage = ({ data, pageContext }) => {
className="hover:underline text-vibrant-light hover:text-muted-light arrow-left-before mr-1" className="hover:underline text-vibrant-light hover:text-muted-light arrow-left-before mr-1"
onClick={() => navigate(-1)} onClick={() => navigate(-1)}
type="button" type="button"
>back</button> >
back
</button>
<Link <Link
className="hover:underline text-vibrant-light hover:text-muted-light mx-1" className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
to="/" to="/"
>home</Link> >
home
</Link>
<Link <Link
className="hover:underline text-vibrant-light hover:text-muted-light mx-1" className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
to="/photogallery/" to="/photogallery/"
>gallery <span className="bg-gray-300 text-black">esc</span></Link> >
{pageContext.prevImage && <Link gallery <span className="bg-gray-300 text-black">esc</span>
</Link>
{pageContext.prevImage && (
<Link
className="hover:underline text-vibrant-light hover:text-muted-light mx-1" className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
to={`/photogallery/${pageContext.prevImage}/`} to={`/photogallery/${pageContext.prevImage}/`}
>previous <span className="bg-gray-300 text-black">&#11104;</span></Link>} >
{pageContext.nextImage && <Link previous <span className="bg-gray-300 text-black">&#11104;</span>
</Link>
)}
{pageContext.nextImage && (
<Link
className="hover:underline text-vibrant-light hover:text-muted-light mx-1" className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
to={`/photogallery/${pageContext.nextImage}/`} to={`/photogallery/${pageContext.nextImage}/`}
>next <span className="bg-gray-300 text-black">&#11106;</span></Link>} >
next <span className="bg-gray-300 text-black">&#11106;</span>
</Link>
)}
</nav> </nav>
<div className={classnames('flex', orientationClasses)}> <div className={classnames("flex", orientationClasses)}>
<div className="flex-grow-0"> <div className="flex-grow-0">
<GatsbyImage <GatsbyImage
alt={name} alt={name}
@ -113,36 +137,80 @@ const GalleryImage = ({ data, pageContext }) => {
objectFit="contain" objectFit="contain"
style={{ style={{
maxWidth: `calc(max(90vh, 500px) * ${ar})`, maxWidth: `calc(max(90vh, 500px) * ${ar})`,
maxHeight: '90vh', maxHeight: "90vh",
// minHeight: '500px', // minHeight: '500px',
}} /> }}
/>
</div> </div>
<div className={classnames( <div
'flex-shrink-0 mx-2 flex flex-row portrait:items-end', ar <= 1 className={classnames(
? 'pt-5 flex-col flex-auto text-right' "flex-shrink-0 mx-2 flex flex-row portrait:items-end",
: 'portrait:pt-5 portrait:flex-col portrait:text-right' ar <= 1
)}> ? "pt-5 flex-col flex-auto text-right"
: "portrait:pt-5 portrait:flex-col portrait:text-right"
)}
>
<div className="flex-auto mr-2"> <div className="flex-auto mr-2">
<p className="text-muted-light font-mono text-sm m-0 mt-1">{image.base}</p> <p className="text-muted-light font-mono text-sm m-0 mt-1">
{hasName(image) && <h1 className="text-4xl mt-0 font-serif">{name}</h1>} {image.base}
</p>
{hasName(image) && (
<h1 className="text-4xl mt-0 font-serif">{name}</h1>
)}
<p className="landscape:mr-2">{meta.iptc.caption}</p> <p className="landscape:mr-2">{meta.iptc.caption}</p>
</div> </div>
{<div className="portrait:border-t-2 border-muted-light portrait:mt-2 mr-2 portrait:mb-1" style={{width: 30}}></div>} {
<MetadataItem aspectRatio={ar} data={dateTaken.toLocaleDateString()} icon="calendar-sharp" title="date taken"/> <div
<MetadataItem aspectRatio={ar} data={locationString} icon="location-sharp" title="location"/> className="portrait:border-t-2 border-muted-light portrait:mt-2 mr-2 portrait:mb-1"
<MetadataItem aspectRatio={ar} data={shutterSpeed} icon="stopwatch-sharp" title="shutter speed" /> style={{ width: 30 }}
<MetadataItem aspectRatio={ar} data={`f/${meta.exif.FNumber}`} icon="aperture-sharp" title="aperture" /> ></div>
<MetadataItem aspectRatio={ar} data={meta.exif.ISO} icon="film-outline" title="ISO" /> }
<MetadataItem
aspectRatio={ar}
data={dateTaken.toLocaleDateString()}
icon="calendar-sharp"
title="date taken"
/>
<MetadataItem
aspectRatio={ar}
data={locationString}
icon="location-sharp"
title="location"
/>
<MetadataItem
aspectRatio={ar}
data={shutterSpeed}
icon="stopwatch-sharp"
title="shutter speed"
/>
<MetadataItem
aspectRatio={ar}
data={`f/${meta.exif.FNumber}`}
icon="aperture-sharp"
title="aperture"
/>
<MetadataItem
aspectRatio={ar}
data={meta.exif.ISO}
icon="film-outline"
title="ISO"
/>
</div> </div>
</div> </div>
<div></div> <div></div>
</div> </div>
</>); </>
);
}; };
export const query = graphql` export const query = graphql`
query GalleryImage($imageFilename: String) { query GalleryImage($imageFilename: String) {
allFile(filter: {sourceInstanceName: {eq: "gallery"}, base: {eq: $imageFilename}}) { allFile(
filter: {
sourceInstanceName: { eq: "gallery" }
base: { eq: $imageFilename }
}
) {
edges { edges {
node { node {
base base
@ -182,8 +250,6 @@ export const query = graphql`
} }
} }
} }
`; `;
export default GalleryImage; export default GalleryImage;

View File

@ -1,14 +1,13 @@
import classNames from 'classnames'; import classNames from "classnames";
import React from 'react'; import React from "react";
const MetadataItem = ({ const MetadataItem = ({ aspectRatio, icon, data, title }) =>
aspectRatio, data ? (
icon, <div
data, className={classNames(
title, "flex items-baseline ml-2 text-lg",
}) => data ? ( aspectRatio <= 1 ? "flex-row-reverse" : "portrait:flex-row-reverse"
<div className={classNames('flex items-baseline ml-2 text-lg', )}
aspectRatio <= 1 ? 'flex-row-reverse' : 'portrait:flex-row-reverse')}
title={title} title={title}
> >
<span className="icon-offset mr-1"> <span className="icon-offset mr-1">

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from "react";
import classnames from 'classnames'; import classnames from "classnames";
export const HeroA = ({ export const HeroA = ({
href, href,
@ -9,8 +9,14 @@ export const HeroA = ({
...linkProps ...linkProps
}) => ( }) => (
<a <a
className={classnames('mx-2 underline', isClient && 'text-muted-light hover:text-vibrant-light', className)} className={classnames(
"mx-2 underline",
isClient && "text-muted-light hover:text-vibrant-light",
className
)}
href={href} href={href}
{...linkProps} {...linkProps}
>{children}</a> >
{children}
</a>
); );

View File

@ -1,33 +1,41 @@
import * as React from 'react'; import * as React from "react";
import { Link } from 'gatsby'; import { Link } from "gatsby";
import { GatsbyImage, getImage } from 'gatsby-plugin-image'; import { GatsbyImage, getImage } from "gatsby-plugin-image";
import * as R from 'ramda'; import * as R from "ramda";
import { getAspectRatio, getName } from '../utils'; import { getAspectRatio, getName } from "../utils";
import useBreakpoint from 'use-breakpoint'; import useBreakpoint from "use-breakpoint";
import themeBreakpoints from '../breakpoints'; import themeBreakpoints from "../breakpoints";
const MasonryGallery = ({ images, itemsPerRow: itemsPerRowByBreakpoint }) => { const MasonryGallery = ({ images, itemsPerRow: itemsPerRowByBreakpoint }) => {
const breakpoints = React.useMemo(() => const breakpoints = React.useMemo(
R.pick(R.keys(itemsPerRowByBreakpoint), themeBreakpoints) () => R.pick(R.keys(itemsPerRowByBreakpoint), themeBreakpoints),
, [itemsPerRowByBreakpoint]); [itemsPerRowByBreakpoint]
);
const { breakpoint } = useBreakpoint(breakpoints, 'sm'); const { breakpoint } = useBreakpoint(breakpoints, "sm");
const aspectRatios = React.useMemo(() => R.map(getAspectRatio, images), [images]); const aspectRatios = React.useMemo(
const rowAspectRatioSumsByBreakpoint = React.useMemo(() => R.map(R.pipe( () => R.map(getAspectRatio, images),
R.splitEvery(R.__, aspectRatios), [images]
R.map(R.sum) );
))(itemsPerRowByBreakpoint), [aspectRatios, itemsPerRowByBreakpoint]); const rowAspectRatioSumsByBreakpoint = React.useMemo(
() =>
R.map(R.pipe(R.splitEvery(R.__, aspectRatios), R.map(R.sum)))(
itemsPerRowByBreakpoint
),
[aspectRatios, itemsPerRowByBreakpoint]
);
const itemsPerRow = itemsPerRowByBreakpoint[breakpoint]; const itemsPerRow = itemsPerRowByBreakpoint[breakpoint];
const rowAspectRatioSumsForCurrentBP = rowAspectRatioSumsByBreakpoint[breakpoint]; const rowAspectRatioSumsForCurrentBP =
rowAspectRatioSumsByBreakpoint[breakpoint];
return ( return (
<div <div
className="w-full" className="w-full"
style={{ style={{
position: 'relative', position: "relative",
}} }}
// style={{ maxWidth: minWidth }} // style={{ maxWidth: minWidth }}
> >
@ -36,11 +44,12 @@ const MasonryGallery = ({ images, itemsPerRow: itemsPerRowByBreakpoint }) => {
const rowAspectRatioSum = rowAspectRatioSumsForCurrentBP[rowIndex]; const rowAspectRatioSum = rowAspectRatioSumsForCurrentBP[rowIndex];
// const width = ((getAspectRatio(image) / rowAspectRatioSum) * 100).toFixed(10); // const width = ((getAspectRatio(image) / rowAspectRatioSum) * 100).toFixed(10);
const ar = getAspectRatio(image); const ar = getAspectRatio(image);
const widthNumber = rowAspectRatioSum === ar const widthNumber =
// image is only one in row rowAspectRatioSum === ar
? 100 / itemsPerRow ? // image is only one in row
// image is one of several in row 100 / itemsPerRow
: ((ar / rowAspectRatioSum) * 100).toFixed(5); : // image is one of several in row
((ar / rowAspectRatioSum) * 100).toFixed(5);
const width = `${widthNumber}%`; const width = `${widthNumber}%`;
return ( return (
@ -51,7 +60,8 @@ const MasonryGallery = ({ images, itemsPerRow: itemsPerRowByBreakpoint }) => {
style={{ style={{
width, width,
// marginLeft: '8px', // marginLeft: '8px',
}} to={`/photogallery/${image.base}`} }}
to={`/photogallery/${image.base}`}
> >
<GatsbyImage <GatsbyImage
alt={getName(image)} alt={getName(image)}
@ -63,7 +73,8 @@ const MasonryGallery = ({ images, itemsPerRow: itemsPerRowByBreakpoint }) => {
</Link> </Link>
); );
})} })}
</div>); </div>
);
// return null; // return null;
}; };

View File

@ -1,10 +1,10 @@
import * as React from 'react'; import * as React from "react";
import classnames from 'classnames'; import classnames from "classnames";
import { MDXProvider } from '@mdx-js/react'; import { MDXProvider } from "@mdx-js/react";
import '../../styles/resume.css'; import "../../styles/resume.css";
const MyH1 = props => <h1 style={{ color: 'tomato' }} {...props} />; const MyH1 = (props) => <h1 style={{ color: "tomato" }} {...props} />;
// const MyParagraph = props => ( // const MyParagraph = props => (
// <p style={{ fontSize: '18px', lineHeight: 1.6 }} {...props} /> // <p style={{ fontSize: '18px', lineHeight: 1.6 }} {...props} />
// ); // );
@ -14,12 +14,13 @@ const components = {
// p: MyParagraph, // p: MyParagraph,
}; };
const ResumeLayout = ({ pageContext, children }) => { const ResumeLayout = ({ pageContext, children }) => {
console.log('pc', pageContext); console.log("pc", pageContext);
return ( return (
<MDXProvider components={components}> <MDXProvider components={components}>
<div className={classnames('font-serif container mx-auto resume')}>{children}</div> <div className={classnames("font-serif container mx-auto resume")}>
{children}
</div>
</MDXProvider> </MDXProvider>
); );
}; };

View File

@ -1,4 +1,4 @@
import { graphql } from 'gatsby'; import { graphql } from "gatsby";
export const VibrantColorsFragment = graphql` export const VibrantColorsFragment = graphql`
fragment VibrantColors on FileFieldsImageMetaVibrant { fragment VibrantColors on FileFieldsImageMetaVibrant {

View File

@ -1,7 +1,8 @@
import React from 'react'; import React from "react";
import PropTypes from 'prop-types'; import PropTypes from "prop-types";
const env = process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || 'development'; const env =
process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || "development";
export default function HTML(props) { export default function HTML(props) {
return ( return (
@ -9,25 +10,40 @@ export default function HTML(props) {
<head> <head>
<meta charSet="utf-8" /> <meta charSet="utf-8" />
<meta content="ie=edge" httpEquiv="x-ua-compatible" /> <meta content="ie=edge" httpEquiv="x-ua-compatible" />
<meta content="Chuck Dries: Full Stack Software Engineer and Photographer" name="description" /> <meta
content="Chuck Dries: Full Stack Software Engineer and Photographer"
name="description"
/>
<meta <meta
content="width=device-width, initial-scale=1, shrink-to-fit=no" content="width=device-width, initial-scale=1, shrink-to-fit=no"
name="viewport" name="viewport"
/> />
{props.headComponents} {props.headComponents}
{env === 'production' && <script async data-domain="chuckdries.com" defer src="https://analytics.chuckdries.com/js/plausible.js"></script>} {env === "production" && (
<script
async
data-domain="chuckdries.com"
defer
src="https://analytics.chuckdries.com/js/plausible.js"
></script>
)}
{/* eslint-disable-next-line */} {/* eslint-disable-next-line */}
{env === 'production' && <script>{`window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }`}</script>} {env === "production" && (
<script>{`window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }`}</script>
)}
</head> </head>
<body {...props.bodyAttributes}> <body {...props.bodyAttributes}>
{props.preBodyComponents} {props.preBodyComponents}
<div <div
dangerouslySetInnerHTML={{ __html: props.body }} dangerouslySetInnerHTML={{ __html: props.body }}
id="___gatsby" id="___gatsby"
key={'body'} key={"body"}
/> />
{props.postBodyComponents} {props.postBodyComponents}
<script src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js" type="module"></script> <script
src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js"
type="module"
></script>
</body> </body>
</html> </html>
); );

View File

@ -1,11 +1,11 @@
import * as React from 'react'; import * as React from "react";
import { Link } from 'gatsby'; import { Link } from "gatsby";
// styles // styles
const pageStyles = { const pageStyles = {
color: '#232129', color: "#232129",
padding: '96px', padding: "96px",
fontFamily: '-apple-system, Roboto, sans-serif, serif', fontFamily: "-apple-system, Roboto, sans-serif, serif",
}; };
const headingStyles = { const headingStyles = {
marginTop: 0, marginTop: 0,
@ -17,10 +17,10 @@ const paragraphStyles = {
marginBottom: 48, marginBottom: 48,
}; };
const codeStyles = { const codeStyles = {
color: '#8A6534', color: "#8A6534",
padding: 4, padding: 4,
backgroundColor: '#FFF4DB', backgroundColor: "#FFF4DB",
fontSize: '1.25rem', fontSize: "1.25rem",
borderRadius: 4, borderRadius: 4,
}; };
@ -31,13 +31,13 @@ const NotFoundPage = () => {
<title>Not found</title> <title>Not found</title>
<h1 style={headingStyles}>Page not found</h1> <h1 style={headingStyles}>Page not found</h1>
<p style={paragraphStyles}> <p style={paragraphStyles}>
Sorry{' '} Sorry{" "}
<span aria-label="Pensive emoji" role="img"> <span aria-label="Pensive emoji" role="img">
😔 😔
</span>{' '} </span>{" "}
we couldnt find what you were looking for. we couldnt find what you were looking for.
<br /> <br />
{process.env.NODE_ENV === 'development' ? ( {process.env.NODE_ENV === "development" ? (
<> <>
<br /> <br />
Try creating a page in <code style={codeStyles}>src/pages/</code>. Try creating a page in <code style={codeStyles}>src/pages/</code>.

View File

@ -2,8 +2,8 @@
title: Charles Dries Resume title: Charles Dries Resume
--- ---
import ResumeLayout from '../components/resume/ResumeLayout' import ResumeLayout from "../components/resume/ResumeLayout";
export default ResumeLayout export default ResumeLayout;
# Hello, World! # Hello, World!

View File

@ -1,47 +1,69 @@
import * as React from 'react'; import * as React from "react";
import { Link, graphql } from 'gatsby'; import { Link, graphql } from "gatsby";
import { GatsbyImage, getImage } from 'gatsby-plugin-image'; import { GatsbyImage, getImage } from "gatsby-plugin-image";
import { Helmet } from 'react-helmet'; import { Helmet } from "react-helmet";
import { take } from 'ramda'; import { take } from "ramda";
import classnames from 'classnames'; import classnames from "classnames";
import posthog from 'posthog-js'; import posthog from "posthog-js";
import { getVibrantToHelmetSafeBodyStyle, getVibrant, getAspectRatio } from '../utils'; import {
import { HeroA } from '../components/Index/HeroLink'; getVibrantToHelmetSafeBodyStyle,
getVibrant,
getAspectRatio,
} from "../utils";
import { HeroA } from "../components/Index/HeroLink";
const env = process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || 'development'; const env =
process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || "development";
const getDifferentRand = (range, lastNs, iterations = 0) => { const getDifferentRand = (range, lastNs, iterations = 0) => {
const n = Math.floor(Math.random() * range); const n = Math.floor(Math.random() * range);
if (lastNs.findIndex(x => x === n) > -1 && iterations < 5) { if (lastNs.findIndex((x) => x === n) > -1 && iterations < 5) {
console.log('got dupe, trying again', n); console.log("got dupe, trying again", n);
return getDifferentRand(range, lastNs, iterations + 1); return getDifferentRand(range, lastNs, iterations + 1);
} }
return n; return n;
}; };
const IndexPage = ({ data: { allFile: { edges } } }) => { const IndexPage = ({
data: {
allFile: { edges },
},
}) => {
const [isClient, setIsClient] = React.useState(false); const [isClient, setIsClient] = React.useState(false);
const [imageIndex, setImageIndex] = React.useState(0); const [imageIndex, setImageIndex] = React.useState(0);
const images = React.useMemo(() => edges.map((edge) => edge.node), [edges]); const images = React.useMemo(() => edges.map((edge) => edge.node), [edges]);
const image = React.useMemo(() => { const image = React.useMemo(() => {
console.log('ii', imageIndex); console.log("ii", imageIndex);
return images[imageIndex]; return images[imageIndex];
}, [images, imageIndex]); }, [images, imageIndex]);
const shuffleImage = React.useCallback((currentImage) => { const shuffleImage = React.useCallback(
const lastThreeImages = JSON.parse(localStorage.getItem('lastHeros')) || []; (currentImage) => {
if (env === 'production') { const lastThreeImages =
JSON.parse(localStorage.getItem("lastHeros")) || [];
if (env === "production") {
try { try {
// eslint-disable-next-line // eslint-disable-next-line
posthog.capture('[shuffle image]', { currentImage: currentImage?.base }); posthog.capture("[shuffle image]", {
window.plausible('Shuffle', {props: { currentImage: currentImage?.base }}); currentImage: currentImage?.base,
} catch (e) {/* do nothing */} });
window.plausible("Shuffle", {
props: { currentImage: currentImage?.base },
});
} catch (e) {
/* do nothing */
}
} }
const index = getDifferentRand(images.length, lastThreeImages); const index = getDifferentRand(images.length, lastThreeImages);
localStorage.setItem('lastHeros', JSON.stringify(take(3, [index, ...lastThreeImages]))); localStorage.setItem(
"lastHeros",
JSON.stringify(take(3, [index, ...lastThreeImages]))
);
setImageIndex(index); setImageIndex(index);
}, [images.length]); },
[images.length]
);
// pick random image on page hydration // pick random image on page hydration
React.useEffect(() => { React.useEffect(() => {
@ -54,7 +76,7 @@ const IndexPage = ({ data: { allFile: { edges } } }) => {
React.useEffect(() => { React.useEffect(() => {
const keyListener = (e) => { const keyListener = (e) => {
switch (e.code) { switch (e.code) {
case 'ArrowRight': { case "ArrowRight": {
if (imageIndex === images.length - 1) { if (imageIndex === images.length - 1) {
setImageIndex(0); setImageIndex(0);
return; return;
@ -63,7 +85,7 @@ const IndexPage = ({ data: { allFile: { edges } } }) => {
return; return;
} }
case 'ArrowLeft': { case "ArrowLeft": {
if (imageIndex === 0) { if (imageIndex === 0) {
setImageIndex(images.length - 1); setImageIndex(images.length - 1);
return; return;
@ -73,59 +95,86 @@ const IndexPage = ({ data: { allFile: { edges } } }) => {
} }
} }
}; };
document.addEventListener('keydown', keyListener); document.addEventListener("keydown", keyListener);
return () => { return () => {
document.removeEventListener('keydown', keyListener); document.removeEventListener("keydown", keyListener);
}; };
}, [imageIndex, images.length]); }, [imageIndex, images.length]);
const vibrant = getVibrant(image); const vibrant = getVibrant(image);
const ar = getAspectRatio(image); const ar = getAspectRatio(image);
return (<> return (
<>
<Helmet> <Helmet>
<title>Chuck Dries</title> <title>Chuck Dries</title>
<body <body
className={classnames(isClient ? 'bg-vibrant-dark' : '')} className={classnames(isClient ? "bg-vibrant-dark" : "")}
style={getVibrantToHelmetSafeBodyStyle(vibrant)} style={getVibrantToHelmetSafeBodyStyle(vibrant)}
/> />
</Helmet> </Helmet>
{/* WIP: ipad portrait hits md breakpoint, looks bad */} {/* WIP: ipad portrait hits md breakpoint, looks bad */}
<main <main
className={classnames('font-serif hero', ar > 1 || !isClient className={classnames(
? 'landscape:grid portrait:flex portrait:flex-col' : 'portrait:grid landscape:flex landscape:flex-row-reverse')} "font-serif hero",
ar > 1 || !isClient
? "landscape:grid portrait:flex portrait:flex-col"
: "portrait:grid landscape:flex landscape:flex-row-reverse"
)}
> >
{isClient ? {isClient ? (
<GatsbyImage <GatsbyImage
alt="" alt=""
className={classnames( className={classnames(
ar > 1 || !isClient ? 'landscape:h-screen portrait:h-two-thirds-vw' : 'h-screen portrait:w-full landscape:w-1/2', ar > 1 || !isClient
? "landscape:h-screen portrait:h-two-thirds-vw"
: "h-screen portrait:w-full landscape:w-1/2"
)} )}
image={getImage(image)} image={getImage(image)}
loading="eager" loading="eager"
style={{ style={{
gridArea: '1/1', gridArea: "1/1",
}} /> }}
/>
) : (
// 67vw = 1/1.49253731 = 1/aspect ratio of my camera lol // 67vw = 1/1.49253731 = 1/aspect ratio of my camera lol
: <div className="landscape:h-screen portrait:h-two-thirds-vw w-full" style={{gridArea: '1/1' }}></div> } <div
<div className={classnames('relative grid', ar <= 1 ? 'place-items-end landscape:place-items-center' : 'place-items-end')} style={{gridArea: '1/1'}}> className="landscape:h-screen portrait:h-two-thirds-vw w-full"
style={{ gridArea: "1/1" }}
></div>
)}
<div
className={classnames(
"relative grid",
ar <= 1
? "place-items-end landscape:place-items-center"
: "place-items-end"
)}
style={{ gridArea: "1/1" }}
>
<div className=""> <div className="">
<div className="flex mx-6 justify-end"> <div className="flex mx-6 justify-end">
<div className="flex my-2 items-center flex-col"> <div className="flex my-2 items-center flex-col">
<Link <Link
className={classnames( className={classnames(
'hover:underline inline-block px-1 my-1 mr-2 text-md rounded-md border-2', "hover:underline inline-block px-1 my-1 mr-2 text-md rounded-md border-2",
isClient && 'text-muted-dark bg-muted-light hover:border-muted border-muted-dark')} isClient &&
"text-muted-dark bg-muted-light hover:border-muted border-muted-dark"
)}
// style={{top: '5px'}} // style={{top: '5px'}}
id="image-link" id="image-link"
title="view image details" title="view image details"
to={`/photogallery/${image.base}/`} to={`/photogallery/${image.base}/`}
> >
<span className="icon-offset"><ion-icon name="image"></ion-icon></span> <span className="icon-offset">
<ion-icon name="image"></ion-icon>
</span>
</Link> </Link>
<button <button
className={classnames( className={classnames(
'hover:underline inline-block px-1 my-1 mr-2 text-md rounded-md border-2', "hover:underline inline-block px-1 my-1 mr-2 text-md rounded-md border-2",
isClient && 'text-muted-dark bg-muted-light hover:border-muted border-muted-dark')} isClient &&
"text-muted-dark bg-muted-light hover:border-muted border-muted-dark"
)}
id="shuffle-button" id="shuffle-button"
onClick={() => { onClick={() => {
shuffleImage(image); shuffleImage(image);
@ -133,13 +182,17 @@ const IndexPage = ({ data: { allFile: { edges } } }) => {
title="shuffle image" title="shuffle image"
type="button" type="button"
> >
<span className="icon-offset"><ion-icon name="shuffle"></ion-icon></span> <span className="icon-offset">
<ion-icon name="shuffle"></ion-icon>
</span>
</button> </button>
</div> </div>
<Link <Link
className={classnames( className={classnames(
'hover:underline p-3 px-5 py-4 my-3 text-md sm:text-lg rounded-md border-2 arrow-right-after font-bold font-serif', "hover:underline p-3 px-5 py-4 my-3 text-md sm:text-lg rounded-md border-2 arrow-right-after font-bold font-serif",
isClient && 'text-muted-dark bg-muted-light hover:border-muted border-muted-dark')} isClient &&
"text-muted-dark bg-muted-light hover:border-muted border-muted-dark"
)}
id="photogallery-link" id="photogallery-link"
to="/photogallery/" to="/photogallery/"
> >
@ -148,38 +201,99 @@ const IndexPage = ({ data: { allFile: { edges } } }) => {
</div> </div>
<section <section
className={classnames( className={classnames(
ar > 1 && 'landscape:shadow-lg', ar > 1 && "landscape:shadow-lg",
'md:px-6 px-4 md:py-5 py-3 rounded-l-md mb-4', isClient && "md:px-6 px-4 md:py-5 py-3 rounded-l-md mb-4",
'bg-vibrant-dark bg-opacity-60 backdrop-filter backdrop-blur-xl' isClient &&
"bg-vibrant-dark bg-opacity-60 backdrop-filter backdrop-blur-xl"
)} )}
> >
<div <div
className={classnames('mx-auto filter drop-shadow items-end', ar > 1 || !isClient ? 'landscape:flex' : 'portrait:flex')} className={classnames(
"mx-auto filter drop-shadow items-end",
ar > 1 || !isClient ? "landscape:flex" : "portrait:flex"
)}
> >
<div className="mr-5 flex-auto"> <div className="mr-5 flex-auto">
<h1 className={classnames('font-black text-4xl sm:text-5xl md:text-6xl', isClient && 'text-vibrant-light')}>Chuck Dries</h1> <h1
<h2 className={classnames('text-xl md:text-2xl', isClient && 'text-vibrant')}>Full Stack Software Engineer &amp; Hobbyist Photographer</h2> className={classnames(
"font-black text-4xl sm:text-5xl md:text-6xl",
isClient && "text-vibrant-light"
)}
>
Chuck Dries
</h1>
<h2
className={classnames(
"text-xl md:text-2xl",
isClient && "text-vibrant"
)}
>
Full Stack Software Engineer &amp; Hobbyist Photographer
</h2>
</div> </div>
{/* {<div className="border-t-2 border-muted-light mt-2 mr-2 mb-1" style={{width: 30}}></div>} */} {/* {<div className="border-t-2 border-muted-light mt-2 mr-2 mb-1" style={{width: 30}}></div>} */}
<ul className={'md:mr-4', classnames(isClient && 'text-muted-light')}> <ul
className={
("md:mr-4", classnames(isClient && "text-muted-light"))
}
>
<li>Software Engineer, Axosoft</li> <li>Software Engineer, Axosoft</li>
<li><HeroA className="ml-0" href="mailto:chuck@chuckdries.com" isClient={isClient}>chuck@chuckdries.com</HeroA>/<span className="ml-2">602.618.0414</span></li>
<li> <li>
<HeroA className="ml-0" href="http://github.com/chuckdries" isClient={isClient}>Github</HeroA>/ <HeroA
<HeroA href="https://www.linkedin.com/in/chuckdries/" isClient={isClient}>LinkedIn</HeroA>/ className="ml-0"
<HeroA href="https://devpost.com/chuckdries" isClient={isClient}>Devpost</HeroA>/ href="mailto:chuck@chuckdries.com"
<HeroA href="/CharlesDriesResumeCurrent.pdf" isClient={isClient}>Resume [pdf]</HeroA>/ isClient={isClient}
<HeroA href="https://medium.com/@chuckdries" isClient={isClient}>Medium (blog)</HeroA> >
chuck@chuckdries.com
</HeroA>
/<span className="ml-2">602.618.0414</span>
</li>
<li>
<HeroA
className="ml-0"
href="http://github.com/chuckdries"
isClient={isClient}
>
Github
</HeroA>
/
<HeroA
href="https://www.linkedin.com/in/chuckdries/"
isClient={isClient}
>
LinkedIn
</HeroA>
/
<HeroA
href="https://devpost.com/chuckdries"
isClient={isClient}
>
Devpost
</HeroA>
/
<HeroA
href="/CharlesDriesResumeCurrent.pdf"
isClient={isClient}
>
Resume [pdf]
</HeroA>
/
<HeroA
href="https://medium.com/@chuckdries"
isClient={isClient}
>
Medium (blog)
</HeroA>
</li> </li>
</ul> </ul>
</div> </div>
</section> </section>
</div> </div>
</div> </div>
</main> </main>
</>); </>
);
}; };
export const query = graphql` export const query = graphql`

View File

@ -1,20 +1,21 @@
import * as React from 'react'; import * as React from "react";
import { graphql, Link } from 'gatsby'; import { graphql, Link } from "gatsby";
import { navigate } from 'gatsby'; import { navigate } from "gatsby";
import { Helmet } from 'react-helmet'; import { Helmet } from "react-helmet";
import MasonryGallery from '../components/MasonryGallery'; import MasonryGallery from "../components/MasonryGallery";
// TODO: caption and title more images // TODO: caption and title more images
// TODO: more images // TODO: more images
const GalleryPage = ({ data }) => { const GalleryPage = ({ data }) => {
const images = React.useMemo(() => const images = React.useMemo(
data.allFile.edges () => data.allFile.edges.map((edge) => edge.node, [data]),
.map(edge => edge.node, [data]) [data]
, [data]); );
return (<> return (
<>
<Helmet> <Helmet>
<title>Photo Gallery | Chuck Dries</title> <title>Photo Gallery | Chuck Dries</title>
<body className="bg-black text-white" /> <body className="bg-black text-white" />
@ -24,18 +25,26 @@ const GalleryPage = ({ data }) => {
className="hover:underline text-vibrant-light hover:text-muted-light arrow-left-before mr-1" className="hover:underline text-vibrant-light hover:text-muted-light arrow-left-before mr-1"
onClick={() => navigate(-1)} onClick={() => navigate(-1)}
type="button" type="button"
>back</button> >
back
</button>
<Link <Link
className="hover:underline text-vibrant-light hover:text-muted-light mx-1" className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
to="/" to="/"
>home</Link> >
home
</Link>
<Link <Link
className="hover:underline text-vibrant-light hover:text-muted-light mx-1" className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
to="/photogallery/" to="/photogallery/"
>gallery</Link> >
gallery
</Link>
</nav> </nav>
<div className="bg-black min-h-screen mx-auto 2xl:container"> <div className="bg-black min-h-screen mx-auto 2xl:container">
<h1 className="text-5xl mt-0 ml-5 font-serif font-black z-10 relative">Photo Gallery</h1> <h1 className="text-5xl mt-0 ml-5 font-serif font-black z-10 relative">
Photo Gallery
</h1>
<div className="mx-auto"> <div className="mx-auto">
<MasonryGallery <MasonryGallery
images={images} images={images}
@ -44,12 +53,13 @@ const GalleryPage = ({ data }) => {
md: 2, md: 2,
lg: 3, lg: 3,
xl: 3, xl: 3,
'2xl': 4, "2xl": 4,
}} }}
/> />
</div> </div>
</div> </div>
</>); </>
);
}; };
export const query = graphql` export const query = graphql`
@ -66,10 +76,7 @@ query GalleryPageQuery {
fluid { fluid {
aspectRatio aspectRatio
} }
gatsbyImageData( gatsbyImageData(layout: CONSTRAINED, height: 550)
layout: CONSTRAINED
height: 550
)
} }
fields { fields {
imageMeta { imageMeta {
@ -82,6 +89,7 @@ query GalleryPageQuery {
} }
} }
} }
}`; }
`;
export default GalleryPage; export default GalleryPage;

View File

@ -1,6 +1,6 @@
/* @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital@0;1&display=swap'); */ /* @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital@0;1&display=swap'); */
/* black, bold, regular */ /* black, bold, regular */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&display=swap'); @import url("https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&display=swap");
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@ -30,7 +30,7 @@
scroll-snap-align: start; scroll-snap-align: start;
} }
.scroll-padding-6 { .scroll-padding-6 {
scroll-padding: theme('spacing.6'); scroll-padding: theme("spacing.6");
} }
@variants responsive { @variants responsive {
.h-two-thirds-vw { .h-two-thirds-vw {
@ -63,7 +63,7 @@ a {
margin-left: 3px; margin-left: 3px;
transform: translate(0px); transform: translate(0px);
display: inline-block; display: inline-block;
transition: all .2s; transition: all 0.2s;
} }
.arrow-left-before:before { .arrow-left-before:before {

View File

@ -3,4 +3,3 @@
@apply text-3xl font-bold; @apply text-3xl font-bold;
} }
} }

View File

@ -2,31 +2,36 @@
export const getMeta = (image) => image.fields.imageMeta; export const getMeta = (image) => image.fields.imageMeta;
export const getName = (image) => getMeta(image)?.iptc.object_name || image.base; export const getName = (image) =>
getMeta(image)?.iptc.object_name || image.base;
// some pleasing default colors for SSR and initial hydration // some pleasing default colors for SSR and initial hydration
export const getVibrant = (image) => getMeta(image)?.vibrant; export const getVibrant = (image) => getMeta(image)?.vibrant;
export const hasName = (image) => Boolean(getMeta(image)?.iptc.object_name); export const hasName = (image) => Boolean(getMeta(image)?.iptc.object_name);
export const getAspectRatio = (image) => image.childImageSharp.fluid.aspectRatio; export const getAspectRatio = (image) =>
image.childImageSharp.fluid.aspectRatio;
export const getRgba = (palette, alpha) => `rgba(${palette[0]}, ${palette[1]}, ${palette[2]}, ${alpha || 1})`; export const getRgba = (palette, alpha) =>
`rgba(${palette[0]}, ${palette[1]}, ${palette[2]}, ${alpha || 1})`;
// work around SSR bug in react-helmet // work around SSR bug in react-helmet
export const getVibrantToHelmetSafeBodyStyle = (vibrant) => { export const getVibrantToHelmetSafeBodyStyle = (vibrant) => {
const style = { const style = {
'--muted': vibrant.Muted, "--muted": vibrant.Muted,
'--dark-muted': vibrant.DarkMuted, "--dark-muted": vibrant.DarkMuted,
'--light-muted': vibrant.LightMuted, "--light-muted": vibrant.LightMuted,
'--vibrant': vibrant.Vibrant, "--vibrant": vibrant.Vibrant,
'--dark-vibrant': vibrant.DarkVibrant, "--dark-vibrant": vibrant.DarkVibrant,
'--light-vibrant': vibrant.LightVibrant, "--light-vibrant": vibrant.LightVibrant,
}; };
if (typeof window === 'undefined') { if (typeof window === "undefined") {
return style; return style;
} }
return Object.keys(style).map(key => `${(key)}: ${style[key]}`).join(';'); return Object.keys(style)
.map((key) => `${key}: ${style[key]}`)
.join(";");
}; };
const gcd = (a, b) => { const gcd = (a, b) => {

View File

@ -6,7 +6,8 @@
<body> <body>
<h1>contenteditable can be applied to visible style tags</h1> <h1>contenteditable can be applied to visible style tags</h1>
<p contenteditable="true"> <p contenteditable="true">
This paragraph is editable, as is the style tag below. Edit it to see the layout change in real time. This paragraph is editable, as is the style tag below. Edit it to see the
layout change in real time.
</p> </p>
<pre> <pre>
<style contenteditable="true"> <style contenteditable="true">

View File

@ -1,10 +1,9 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Test Canvas Game</title> <title>Test Canvas Game</title>
<style> <style>
body { body {
@ -15,15 +14,14 @@
</head> </head>
<body> <body>
<canvas id="gc"> <canvas id="gc"> </canvas>
</canvas>
<script> <script>
//setup //setup
c = document.getElementById("gc"); c = document.getElementById("gc");
cc = c.getContext('2d'); cc = c.getContext("2d");
c.height = window.innerHeight; c.height = window.innerHeight;
c.width = window.innerWidth; c.width = window.innerWidth;
c.addEventListener('mousemove', function(e) { c.addEventListener("mousemove", function (e) {
p1y = e.clientY - ph / 2; p1y = e.clientY - ph / 2;
}); });
@ -40,8 +38,6 @@
score1 = score2 = 0; score1 = score2 = 0;
ais = 5; ais = 5;
function reset() { function reset() {
bx = c.width / 2; bx = c.width / 2;
by = c.height / 2; by = c.height / 2;
@ -81,7 +77,7 @@
dy = by - (p1y + ph / 2); dy = by - (p1y + ph / 2);
yv = dy * 10; //.3 yv = dy * 10; //.3
} else { } else {
score2++ score2++;
reset(); reset();
} }
} }
@ -99,16 +95,16 @@
// cc.fillStyle="white"; // cc.fillStyle="white";
cc.fillRect(0, 0, c.width, c.height); cc.fillRect(0, 0, c.width, c.height);
//draw paddles //draw paddles
cc.fillStyle = 'cyan'; cc.fillStyle = "cyan";
cc.fillRect(0, p1y, pt, ph); cc.fillRect(0, p1y, pt, ph);
cc.fillStyle = 'red'; cc.fillStyle = "red";
cc.fillRect(c.width - pt, p2y, pt, ph); cc.fillRect(c.width - pt, p2y, pt, ph);
//draw ball //draw ball
cc.fillStyle = 'lightgreen'; cc.fillStyle = "lightgreen";
cc.fillRect(bx - bd / 2, by - bd / 2, bd, bd); cc.fillRect(bx - bd / 2, by - bd / 2, bd, bd);
//draw scores //draw scores
cc.fillStyle = 'white'; cc.fillStyle = "white";
cc.font = '20px Times' cc.font = "20px Times";
cc.fillText(score1, 100, 100); cc.fillText(score1, 100, 100);
cc.fillText(score2, c.width - 100, 100); cc.fillText(score2, c.width - 100, 100);
//draw framerate //draw framerate
@ -116,9 +112,8 @@
window.requestAnimationFrame(update); //Keep the game running window.requestAnimationFrame(update); //Keep the game running
} }
reset() //prepare the game reset(); //prepare the game
update() //start the game update(); //start the game
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,19 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<title>Document</title> <title>Document</title>
<link rel="stylesheet" href="link/to/css"> <link rel="stylesheet" href="link/to/css" />
</head> </head>
<body> <body>
<a href="">a tags are for links</a> <a href="">a tags are for links</a>
<p>p tags are for paragraphs</p> <p>p tags are for paragraphs</p>
<h1>h1 through h6 are for headers</h1> <h1>h1 through h6 are for headers</h1>
<img src="path/to/image.jpg" alt="imgs are for images"> <img src="path/to/image.jpg" alt="imgs are for images" />
<div>divs are just boxes</div> <div>divs are just boxes</div>
</body> </body>
</html> </html>

View File

@ -1,10 +1,9 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Robotic Chuck</title> <title>Robotic Chuck</title>
<style> <style>
body { body {
@ -35,9 +34,9 @@
#genbutton { #genbutton {
color: white; color: white;
background: #01C106; background: #01c106;
padding: 1em; padding: 1em;
border: 1px solid #1E7931; border: 1px solid #1e7931;
font-size: 1em; font-size: 1em;
border-radius: 3px; border-radius: 3px;
} }
@ -55,23 +54,48 @@
var outBox = document.getElementById("output"); var outBox = document.getElementById("output");
var titleBox = document.getElementById("titleIn"); var titleBox = document.getElementById("titleIn");
var descBox = document.getElementById("description"); var descBox = document.getElementById("description");
var tstring = "<style>.here,.invmenu a{font-style:italic}.here{font-size:14px;font-weight:400;color:grey}.invmenu{padding:5px 20px;margin:10px 0}</style>\n"; var tstring =
tstring = tstring + "<" + "div class=\"invmenu\" style=\"max-width:100%;background: #f2f2f2;\">\n<h3 style=\"text-align:left;\">" + titleBox.value + "</h3>\n<p><em>"; "<style>.here,.invmenu a{font-style:italic}.here{font-size:14px;font-weight:400;color:grey}.invmenu{padding:5px 20px;margin:10px 0}</style>\n";
tstring = tstring + descBox.value + "</em></p>\n<hr style=\"border: 1px solid #cfcfcf\">\n" tstring =
formatter = links.map((object) => "<h4><a href=\"" + object.url + "\">" + object.headline + "</a></h4>\n"); tstring +
"<" +
'div class="invmenu" style="max-width:100%;background: #f2f2f2;">\n<h3 style="text-align:left;">' +
titleBox.value +
"</h3>\n<p><em>";
tstring =
tstring +
descBox.value +
'</em></p>\n<hr style="border: 1px solid #cfcfcf">\n';
formatter = links.map(
(object) =>
'<h4><a href="' +
object.url +
'">' +
object.headline +
"</a></h4>\n"
);
tstring = tstring + formatter.join(""); tstring = tstring + formatter.join("");
tstring = tstring + "</div>" tstring = tstring + "</div>";
console.log(tstring); console.log(tstring);
outBox.innerText = tstring; outBox.innerText = tstring;
outBox.style.minHeight = (formatter.length + 9) + "em"; outBox.style.minHeight = formatter.length + 9 + "em";
document.getElementById("preview").innerHTML = tstring; document.getElementById("preview").innerHTML = tstring;
} }
</script> </script>
<p></p> <p></p>
<p>Title of box:</p> <input type="text" id="titleIn" placeholder="Investigating Hope: The Series"> <p>Title of box:</p>
<input
type="text"
id="titleIn"
placeholder="Investigating Hope: The Series"
/>
<p>Description text:</p> <p>Description text:</p>
<input type="text" id="description" placeholder="This article is one in a series of investigative pieces about a complaint filed with ASU regarding accusations against on-campus ministry Hope Church."> <input
type="text"
id="description"
placeholder="This article is one in a series of investigative pieces about a complaint filed with ASU regarding accusations against on-campus ministry Hope Church."
/>
<p>Enter the links to generate according to this format:</p> <p>Enter the links to generate according to this format:</p>
<pre><code class="json">[ <pre><code class="json">[
{ {
@ -87,20 +111,37 @@
"url": "yet-another-url" "url": "yet-another-url"
} }
]</code></pre> ]</code></pre>
<p>This format is known as JSON, it's an internet standard that's supposed to be easy for humans and computers to read. <p>
Note how commas are used to separate multiple items but are never used after the last item in a set. This is important, This format is known as JSON, it's an internet standard that's supposed to
it will not work if you include a trailing comma where there should not be one.</p> be easy for humans and computers to read. Note how commas are used to
<textarea name="input" id="in" cols="100" rows="30" placeholder="Please be careful, this will not work if you format the JSON incorrectly"></textarea> separate multiple items but are never used after the last item in a set.
<p><a id="genbutton" href="#output" onclick="generate()">Generate Code</a></p> This is important, it will not work if you include a trailing comma where
there should not be one.
</p>
<textarea
name="input"
id="in"
cols="100"
rows="30"
placeholder="Please be careful, this will not work if you format the JSON incorrectly"
></textarea>
<p>
<a id="genbutton" href="#output" onclick="generate()">Generate Code</a>
</p>
<p>Paste the following code into a safeembed</p> <p>Paste the following code into a safeembed</p>
<textarea id="output" cols="100" readonly></textarea> <textarea id="output" cols="100" readonly></textarea>
<p><em> Don't forget:</em> For each page you embed on, add the following bit of code just before the <code>&lt;/h4&gt;</code> at the end of the line on the line that corresponds to the current page.</p> <p>
<em> Don't forget:</em> For each page you embed on, add the following bit
of code just before the <code>&lt;/h4&gt;</code> at the end of the line on
the line that corresponds to the current page.
</p>
<pre><code>&lt;span class="here"&gt;(You are here)&lt;/span&gt;</code></pre> <pre><code>&lt;span class="here"&gt;(You are here)&lt;/span&gt;</code></pre>
<p>The code generated will look roughly like this:</P> <p>The code generated will look roughly like this:</p>
<div id="preview" style="max-width: 800px;"></div> <div id="preview" style="max-width: 800px"></div>
<p>Gryphon overrides certain styles by default, so make sure to test on your article.</p> <p>
Gryphon overrides certain styles by default, so make sure to test on your
article.
</p>
</body> </body>
</html> </html>

View File

@ -1,34 +1,34 @@
const defaultTheme = require('tailwindcss/defaultTheme'); const defaultTheme = require("tailwindcss/defaultTheme");
module.exports = { module.exports = {
purge: ['./src/**/*.{js,jsx,ts,tsx}'], purge: ["./src/**/*.{js,jsx,ts,tsx}"],
// darkMode: 'media', // or 'media' or 'class' // darkMode: 'media', // or 'media' or 'class'
theme: { theme: {
screens: { screens: {
'sm': '640px', sm: "640px",
'md': '768px', md: "768px",
'lg': '1024px', lg: "1024px",
'xl': '1280px', xl: "1280px",
'2xl': '1536px', "2xl": "1536px",
'portrait': {'raw': '(orientation: portrait)'}, portrait: { raw: "(orientation: portrait)" },
'landscape': {'raw': '(orientation: landscape)'}, landscape: { raw: "(orientation: landscape)" },
}, },
spacing: { spacing: {
'0': '0px', 0: "0px",
'1': '4px', 1: "4px",
'2': '8px', 2: "8px",
'3': '12px', 3: "12px",
'4': '16px', 4: "16px",
'5': '24px', 5: "24px",
'6': '32px', 6: "32px",
'7': '48px', 7: "48px",
'8': '80px', 8: "80px",
'9': '800px', 9: "800px",
}, },
fontFamily: { fontFamily: {
...defaultTheme.fontFamily, ...defaultTheme.fontFamily,
// serif: ['Didot', 'Didot LT', 'STD', 'Hoefler Text' , 'Garamond', 'Times New Roman', 'serif'] // serif: ['Didot', 'Didot LT', 'STD', 'Hoefler Text' , 'Garamond', 'Times New Roman', 'serif']
serif: ['Playfair Display', 'serif'], serif: ["Playfair Display", "serif"],
}, },
extend: { extend: {
colors: { colors: {
@ -40,7 +40,7 @@ module.exports = {
if (opacityVariable !== undefined) { if (opacityVariable !== undefined) {
return `rgba(var(--vibrant), var(${opacityVariable}, 1))`; return `rgba(var(--vibrant), var(${opacityVariable}, 1))`;
} }
return 'rgb(var(--vibrant))'; return "rgb(var(--vibrant))";
}, },
light: ({ opacityVariable, opacityValue }) => { light: ({ opacityVariable, opacityValue }) => {
if (opacityValue !== undefined) { if (opacityValue !== undefined) {
@ -49,7 +49,7 @@ module.exports = {
if (opacityVariable !== undefined) { if (opacityVariable !== undefined) {
return `rgba(var(--light-vibrant), var(${opacityVariable}, 1))`; return `rgba(var(--light-vibrant), var(${opacityVariable}, 1))`;
} }
return 'rgb(var(--light-vibrant))'; return "rgb(var(--light-vibrant))";
}, },
dark: ({ opacityVariable, opacityValue }) => { dark: ({ opacityVariable, opacityValue }) => {
if (opacityValue !== undefined) { if (opacityValue !== undefined) {
@ -58,7 +58,7 @@ module.exports = {
if (opacityVariable !== undefined) { if (opacityVariable !== undefined) {
return `rgba(var(--dark-vibrant), var(${opacityVariable}, 1))`; return `rgba(var(--dark-vibrant), var(${opacityVariable}, 1))`;
} }
return 'rgb(var(--dark-vibrant))'; return "rgb(var(--dark-vibrant))";
}, },
}, },
muted: { muted: {
@ -69,7 +69,7 @@ module.exports = {
if (opacityVariable !== undefined) { if (opacityVariable !== undefined) {
return `rgba(var(--muted), var(${opacityVariable}, 1))`; return `rgba(var(--muted), var(${opacityVariable}, 1))`;
} }
return 'rgb(var(--muted))'; return "rgb(var(--muted))";
}, },
light: ({ opacityVariable, opacityValue }) => { light: ({ opacityVariable, opacityValue }) => {
if (opacityValue !== undefined) { if (opacityValue !== undefined) {
@ -78,7 +78,7 @@ module.exports = {
if (opacityVariable !== undefined) { if (opacityVariable !== undefined) {
return `rgba(var(--light-muted), var(${opacityVariable}, 1))`; return `rgba(var(--light-muted), var(${opacityVariable}, 1))`;
} }
return 'rgb(var(--light-muted))'; return "rgb(var(--light-muted))";
}, },
dark: ({ opacityVariable, opacityValue }) => { dark: ({ opacityVariable, opacityValue }) => {
if (opacityValue !== undefined) { if (opacityValue !== undefined) {
@ -87,7 +87,7 @@ module.exports = {
if (opacityVariable !== undefined) { if (opacityVariable !== undefined) {
return `rgba(var(--dark-muted), var(${opacityVariable}, 1))`; return `rgba(var(--dark-muted), var(${opacityVariable}, 1))`;
} }
return 'rgb(var(--dark-muted))'; return "rgb(var(--dark-muted))";
}, },
}, },
}, },