diff --git a/gatsby-browser.js b/gatsby-browser.js index 60a275a..e0d8c12 100644 --- a/gatsby-browser.js +++ b/gatsby-browser.js @@ -16,3 +16,12 @@ export const onRouteUpdate = () => { updateFavicon(e.matches); }); }; + +export const onInitialClientRender = () => { + const storage = localStorage.getItem('theme'); + const theme = storage && JSON.parse(storage); + if (theme) { + const app = document.querySelector('.app'); + app.setAttribute('data-theme', theme); + } +}; diff --git a/gatsby-config.js b/gatsby-config.js index 5f2f53d..615d49f 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -13,8 +13,8 @@ module.exports = { short_name: 'Adam Graham', start_url: '/', display: 'standalone', - theme_color: '#101214', - background_color: '#101214', + theme_color: '#282e34', + background_color: '#ffffff', icon: 'static/logo512.png', }, }, diff --git a/gatsby-node.js b/gatsby-node.js index 1962e7a..a9eb035 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -14,36 +14,19 @@ exports.onCreateWebpackConfig = ({ actions }) => { exports.createSchemaCustomization = ({ actions }) => { const { createTypes } = actions; const typeDefs = ` - type ProjectDetail { - key: String! - value: String! - } - type ProjectButton { - name: String! - link: String! - icon: String - } - type ProjectSection { - title: String - link: String - mainImage: File @fileByRelativePath - mainVideo: String - paragraphs: [String] - gallery: [File] @fileByRelativePath - videos: [String] - } type ArtJson implements Node { id: String! category: String! title: String! - date: String description: String description_short: String description_long: [String] + date: String + role: String + tech: [String] image: File! @fileByRelativePath imageAltText: String imageBorder: String - details: [ProjectDetail] buttons: [ProjectButton] sections: [ProjectSection] } @@ -51,14 +34,15 @@ exports.createSchemaCustomization = ({ actions }) => { id: String! category: String! title: String! - date: String description: String description_short: String description_long: [String] + date: String + role: String + tech: [String] image: File! @fileByRelativePath imageAltText: String imageBorder: String - details: [ProjectDetail] buttons: [ProjectButton] sections: [ProjectSection] } @@ -66,14 +50,15 @@ exports.createSchemaCustomization = ({ actions }) => { id: String! category: String! title: String! - date: String description: String description_short: String description_long: [String] + date: String + role: String + tech: [String] image: File! @fileByRelativePath imageAltText: String imageBorder: String - details: [ProjectDetail] buttons: [ProjectButton] sections: [ProjectSection] } @@ -81,14 +66,15 @@ exports.createSchemaCustomization = ({ actions }) => { id: String! category: String! title: String! - date: String description: String description_short: String description_long: [String] + date: String + role: String + tech: [String] image: File! @fileByRelativePath imageAltText: String imageBorder: String - details: [ProjectDetail] buttons: [ProjectButton] sections: [ProjectSection] } @@ -96,17 +82,45 @@ exports.createSchemaCustomization = ({ actions }) => { id: String! category: String! title: String! - date: String description: String description_short: String description_long: [String] + date: String + role: String + tech: [String] image: File! @fileByRelativePath imageAltText: String imageBorder: String - details: [ProjectDetail] buttons: [ProjectButton] sections: [ProjectSection] } + type ProjectsJson implements Node { + title: String! + projects: [ProjectsItem] + } + type ProjectsItem { + title: String + description: String + date: String + link: String + externalLink: String + tags: [String] + } + type ProjectButton { + name: String! + url: String! + icon: String + } + type ProjectSection { + title: String + link: String + mainImage: File @fileByRelativePath + mainImageLink: String + mainVideo: String + paragraphs: [String] + gallery: [File] @fileByRelativePath + videos: [String] + } `; createTypes(typeDefs); }; diff --git a/src/components/Dock.js b/src/components/Dock.js new file mode 100644 index 0000000..f8bed10 --- /dev/null +++ b/src/components/Dock.js @@ -0,0 +1,89 @@ +import '../styles/dock.css'; +import { Icon, Link, SocialIcon } from '@zigurous/react-components'; +import { Link as GatsbyLink } from 'gatsby'; +import React from 'react'; +import { dockLinks, socialLinks } from '../links'; + +const Dock = ({ theme, toggleTheme, secondaryLinks }) => { + return ( +
+
+ +
+ {socialLinks.map((link) => ( + + ))} +
+ +
+
+ +
+ {theme === 'dark' ? 'Light Mode' : 'Dark Mode'} +
+
+
+
+ {secondaryLinks && ( +
+ {secondaryLinks.map((link) => ( + + ))} +
+ )} +
+ ); +}; + +const DockItem = ({ link, external = false }) => { + return ( +
+ + {link.icon ? ( + + ) : ( + + )} + +
{link.name}
+
+ ); +}; + +export default Dock; diff --git a/src/components/Gallery.js b/src/components/Gallery.js index 69bc048..89836ef 100644 --- a/src/components/Gallery.js +++ b/src/components/Gallery.js @@ -1,25 +1,57 @@ import '../styles/gallery.css'; import { useMediaQuery } from '@zigurous/react-components'; import classNames from 'classnames'; +import { navigate } from 'gatsby'; import PropTypes from 'prop-types'; -import React, { useContext } from 'react'; -import GalleryContext from './GalleryContext'; -import Slide from './Slide'; +import React, { useCallback, useEffect } from 'react'; +import Slide, { SlideProps } from './Slide'; +import { getSessionIndex, setSessionIndex } from '../utils/session'; -const Gallery = ({ className }) => { - const context = useContext(GalleryContext); +const Gallery = ({ category, location, slides = [] }) => { const vertical = useMediaQuery('(max-width: 1365px)'); + + const urlParams = new URLSearchParams(location?.search); + const slideIndex = + (urlParams.has('index') + ? Number.parseInt(urlParams.get('index')) || 0 + : getSessionIndex(category)) % slides.length; + + const currentSlide = + slideIndex >= 0 && slideIndex < slides.length && slides[slideIndex]; + + const setSlideIndex = useCallback( + (index) => { + if (index >= slides.length) index = 0; + if (index < 0) index = slides.length - 1; + setSessionIndex(category, index); + navigate(`/${category}?index=${index}`, { replace: true }); + }, + [category, slides, slideIndex] + ); + + useEffect(() => { + if (!document) return; + const prev = () => setSlideIndex(slideIndex - 1); + const next = () => setSlideIndex(slideIndex + 1); + document.addEventListener('previous_slide', prev); + document.addEventListener('next_slide', next); + return () => { + document.removeEventListener('previous_slide', prev); + document.removeEventListener('next_slide', next); + }; + }, [slideIndex]); + return (
- {context.currentSlide && } + {currentSlide && }
)} - {gallery.slideIndex !== undefined && ( -
- - -
- )} */} -
-
-
- - + + setIsMenuOpen(false)} + /> + ); }; Header.propType = { location: PropTypes.object, - isMenuOpen: PropTypes.bool, - toggleMenu: PropTypes.func, + pageTitle: PropTypes.string, }; export default Header; diff --git a/src/components/Logo.js b/src/components/Logo.js new file mode 100644 index 0000000..e683bf7 --- /dev/null +++ b/src/components/Logo.js @@ -0,0 +1,34 @@ +import '../styles/logo.css'; +import { Link } from '@zigurous/react-components'; +import classNames from 'classnames'; +import { Link as GatsbyLink } from 'gatsby'; +import PropTypes from 'prop-types'; +import React from 'react'; + +const Logo = ({ rounded = false, size = 48 }) => { + return ( +
+ +
+ + + + + +
+ ); +}; + +Logo.propTypes = { + rounded: PropTypes.bool, +}; + +export default Logo; diff --git a/src/components/Menu.js b/src/components/Menu.js deleted file mode 100644 index bd353a7..0000000 --- a/src/components/Menu.js +++ /dev/null @@ -1,179 +0,0 @@ -import '../styles/menu.css'; -import { Link, useModalOverlay } from '@zigurous/react-components'; -import classNames from 'classnames'; -import { Link as GatsbyLink, graphql, navigate, useStaticQuery } from 'gatsby'; -import PropTypes from 'prop-types'; -import React, { useState } from 'react'; -import Header from './Header'; -import { navLinks } from '../links'; - -const query = graphql` - query Menu { - games: allGamesJson { - nodes { - id: jsonId - category - title - image { - sharp: childImageSharp { - original { - src - width - height - } - } - } - imageAltText - imageBorder - } - } - art: allArtJson { - nodes { - id: jsonId - category - title - image { - sharp: childImageSharp { - original { - src - width - height - } - } - } - imageAltText - imageBorder - } - } - websites: allWebsitesJson { - nodes { - id: jsonId - category - title - image { - sharp: childImageSharp { - original { - src - width - height - } - } - } - imageAltText - imageBorder - } - } - tech: allTechJson { - nodes { - id: jsonId - category - title - image { - sharp: childImageSharp { - original { - src - width - height - } - } - } - imageAltText - imageBorder - } - } - presentations: allPresentationsJson { - nodes { - id: jsonId - category - title - image { - sharp: childImageSharp { - original { - src - width - height - } - } - } - imageAltText - imageBorder - } - } - } -`; - -const Menu = ({ location }) => { - const [isOpen, setIsOpen] = useState(false); - const data = useStaticQuery(query); - useModalOverlay(isOpen, true); - return ( - -
-
-
    - {navLinks.map((link) => { - const gallery = data[link.key] || { nodes: [] }; - return ( -
  • - setIsOpen(false)} - tabIndex={isOpen ? 0 : -1} - to={link.to} - unstyled - > - {link.name} - -
    - {gallery.nodes.map((node, index) => ( - - ))} -
    -
  • - ); - })} -
-
-
-
setIsOpen(!isOpen)} - /> - - ); -}; - -Menu.propTypes = { - location: PropTypes.object, -}; - -export default Menu; diff --git a/src/components/MenuGallery.js b/src/components/MenuGallery.js new file mode 100644 index 0000000..97b634f --- /dev/null +++ b/src/components/MenuGallery.js @@ -0,0 +1,185 @@ +import '../styles/menu-gallery.css'; +import { Button, Link, Thumbnail, useModalOverlay } from '@zigurous/react-components'; // prettier-ignore +import classNames from 'classnames'; +import { Link as GatsbyLink, graphql, StaticQuery } from 'gatsby'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { headerLinks } from '../links'; + +const query = graphql` + query Menu { + games: allGamesJson { + nodes { + id: jsonId + category + title + image { + sharp: childImageSharp { + original { + src + width + height + } + } + } + imageAltText + imageBorder + } + } + art: allArtJson { + nodes { + id: jsonId + category + title + image { + sharp: childImageSharp { + original { + src + width + height + } + } + } + imageAltText + imageBorder + } + } + websites: allWebsitesJson { + nodes { + id: jsonId + category + title + image { + sharp: childImageSharp { + original { + src + width + height + } + } + } + imageAltText + imageBorder + } + } + tech: allTechJson { + nodes { + id: jsonId + category + title + image { + sharp: childImageSharp { + original { + src + width + height + } + } + } + imageAltText + imageBorder + } + } + presentations: allPresentationsJson { + nodes { + id: jsonId + category + title + image { + sharp: childImageSharp { + original { + src + width + height + } + } + } + imageAltText + imageBorder + } + } + } +`; + +const MenuGallery = ({ open, onRequestClose = () => {} }) => { + useModalOverlay(open, true); + return ( +
+
+
    + {open && ( + { + return headerLinks.map((link) => { + const gallery = data[link.key] || { nodes: [] }; + return ( +
  • + + {link.name} +
  • + ); + }); + }} + /> + )} +
+
+
+ ); +}; + +MenuGallery.propTypes = { + open: PropTypes.bool, + onRequestClose: PropTypes.func, +}; + +export default MenuGallery; diff --git a/src/components/Metadata.js b/src/components/Metadata.js index ad83cfd..dac5134 100644 --- a/src/components/Metadata.js +++ b/src/components/Metadata.js @@ -20,19 +20,16 @@ const Metadata = (props) => { const metadata = { ...siteMetadata, ...props }; return ( - {/* General tags */} {metadata.title} {metadata.image && } - {/* Open Graph tags */} {metadata.url && } {metadata.image && } - {/* Twitter Card tags */} diff --git a/src/components/Page.js b/src/components/Page.js index 0ec6002..c655705 100644 --- a/src/components/Page.js +++ b/src/components/Page.js @@ -1,54 +1,50 @@ +import { useTheme } from '@zigurous/react-components'; import classNames from 'classnames'; import PropTypes from 'prop-types'; -import React, { useEffect } from 'react'; -import GalleryContext, { useContextState } from './GalleryContext'; -import Menu from './Menu'; +import React from 'react'; +import Dock from './Dock'; +import Header from './Header'; import Metadata from './Metadata'; -import { SlideProps } from './Slide'; -import { getSessionIndex, setSessionIndex } from '../utils/session'; const Page = ({ - category, children, className, - hideNavigation = false, + dockLinks, + hideDock = false, + hideHeader = false, id, location, metadata, - slides, + title, }) => { - const urlParams = new URLSearchParams(location?.search); - const slideIndex = urlParams.has('index') - ? Number.parseInt(urlParams.get('index')) || 0 - : getSessionIndex(category); - const context = useContextState(category, slides, slideIndex); - - useEffect(() => { - setSessionIndex(category, slideIndex); - }, [category, slideIndex]); - + const [theme, _, toggleTheme] = useTheme('light'); return ( - -
- - {!hideNavigation && } -
- {children} -
-
-
+
+ + {!hideHeader &&
} +
+ {children} +
+ {!hideDock && ( + + )} +
); }; Page.propTypes = { - category: PropTypes.string, children: PropTypes.node, className: PropTypes.string, - hideNavigation: PropTypes.bool, + hideDock: PropTypes.bool, + hideHeader: PropTypes.bool, id: PropTypes.string, location: PropTypes.object, metadata: PropTypes.object, - slides: PropTypes.arrayOf(SlideProps), + title: PropTypes.string, }; export default Page; diff --git a/src/components/Project.js b/src/components/Project.js index bff0098..fcc9b7c 100644 --- a/src/components/Project.js +++ b/src/components/Project.js @@ -1,5 +1,5 @@ import '../styles/project.css'; -import { Button, ButtonGroup, EmbeddedYouTube, ImageGallery, Link, ProgressiveImage } from '@zigurous/react-components'; // prettier-ignore +import { Badge, EmbeddedYouTube, ImageGallery, Link, ProgressiveImage } from '@zigurous/react-components'; // prettier-ignore import classNames from 'classnames'; import PropTypes from 'prop-types'; import React from 'react'; @@ -8,52 +8,33 @@ import { ImageProps } from '../types/image'; const Project = ({ className, project }) => (
-

{project.title}

- {project.description && ( -

{project.description}

- )} - {project.description_long && - project.description_long.map((description) => ( -

- {description} -

- ))} - {project.buttons && ( - - {project.buttons.map((button) => ( - - - +

{project.title}

+
+ {project.date} + | + {project.role} +
+ {project.tech && ( +
+ {project.tech.map((tech) => ( + + {tech} + ))} - +
)} -
- {/* {project.details && ( -
-

- {project.details.map((detail, index) => ( - - {index !== 0 &&
} - {detail.key} {detail.value} -
+

+ {project.description && ( +

{project.description}

+ )} + {project.description_long && + project.description_long.map((description) => ( +

+ {description} +

))} -

-
- )} */} +
+ {project.sections && project.sections.map((section, index) => (
@@ -68,14 +49,24 @@ const Project = ({ className, project }) => ( )} )} - {section.mainImage && ( - - )} + {section.mainImage && + (section.mainImageLink ? ( + + + + ) : ( + + ))} {section.mainVideo && ( { + return ( + + ); +}; + +export default ShadowButton; diff --git a/src/components/Slide.js b/src/components/Slide.js index 71e7c1c..b439a9c 100644 --- a/src/components/Slide.js +++ b/src/components/Slide.js @@ -1,9 +1,10 @@ import '../styles/slide.css'; -import { Button, Link, ProgressiveImage } from '@zigurous/react-components'; +import { Link, ProgressiveImage } from '@zigurous/react-components'; import classNames from 'classnames'; import { Link as GatsbyLink } from 'gatsby'; import PropTypes from 'prop-types'; import React from 'react'; +import ShadowButton from './ShadowButton'; import { ImageProps } from '../types/image'; const Slide = ({ className, slide }) => { @@ -34,7 +35,7 @@ const Slide = ({ className, slide }) => {

{slide.date}

-

{slide.title}

+

{slide.title}

{slide.description_short || slide.description}

@@ -44,13 +45,7 @@ const Slide = ({ className, slide }) => { style={{ marginLeft: '-4px' }} unstyled > - + More Details
diff --git a/src/components/index.js b/src/components/index.js index 5f7aa19..2e05575 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,6 +1,10 @@ +export { default as Dock } from './Dock'; export { default as Gallery } from './Gallery'; -export { default as GalleryContext } from './GalleryContext'; -export { default as Menu } from './Menu'; +export { default as Header } from './Header'; +export { default as Logo } from './Logo'; +export { default as MenuGallery } from './MenuGallery'; +export { default as Metadata } from './Metadata'; export { default as Page } from './Page'; export { default as Project, ProjectProps } from './Project'; +export { default as ShadowButton } from './ShadowButton'; export { default as Slide, SlideProps } from './Slide'; diff --git a/src/data/art.json b/src/data/art.json index f7b702b..333897c 100644 --- a/src/data/art.json +++ b/src/data/art.json @@ -2,35 +2,25 @@ { "id": "canvas", "category": "art", - "date": "March 2015", "title": "Canvas", "description": "Canvas is a never-ending, interactive painting. Alter the direction of the brushes to influence the painting or sit back and watch. Every thirty seconds the canvas is cleared and a new set of design principles are created. Learn to let go and design with the flow.", "description_short": "Canvas is a never-ending, interactive painting. Alter the direction of the brushes to influence the painting. Every thirty seconds the canvas is cleared and a new set of design principles are created. Learn to let go and design with the flow.", + "date": "March 2015", + "role": "Solo Project", + "tech": ["Adobe Flash", "ActionScript 3.0"], "image": "../images/banners/canvas-painting.png", "imageAltText": "Canvas Painting", "imageBorder": "none", - "details": [ - { - "key": "ROLE —", - "value": "Solo Project" - }, - { - "key": "TECH —", - "value": "Adobe Flash, ActionScript 3.0" - }, - { - "key": "DATE —", - "value": "March 2015" - } - ], "buttons": [ { "name": "Download", - "link": "https://drive.google.com/file/d/1WG-eI8QvhrFCTdRJlGesNV7vs7nddq6L/view?usp=drive_link" + "url": "https://drive.google.com/file/d/1WG-eI8QvhrFCTdRJlGesNV7vs7nddq6L/view?usp=drive_link", + "icon": "download" }, { "name": "Source Code", - "link": "https://github.com/adamgraham/canvas" + "url": "https://github.com/adamgraham/canvas", + "icon": "code" } ], "sections": [ @@ -49,34 +39,24 @@ { "id": "blackhole", "category": "art", - "date": "January 2015", "title": "Blackhole", "description": "Blackhole is a free-form, avant-garde game that enables players to control particles in unique ways. Individuals explore their own creative design within the art piece, loosely making their own objectives and discovering new ways of controlling this \"creative chaos\" - a new type of game genre.", "description_short": "Blackhole is a free-form, avant-garde game that enables players to control particles in unique ways. Individuals explore their own creative design within the art piece, loosely making their own objectives and discovering new ways of controlling this \"creative chaos\".", + "date": "January 2015", + "role": "Solo Project", + "tech": ["Adobe Flash", "ActionScript 3.0"], "image": "../images/banners/blackhole-painting.jpg", "imageAltText": "Blackhole Painting", - "details": [ - { - "key": "ROLE —", - "value": "Solo Project" - }, - { - "key": "TECH —", - "value": "Adobe Flash, ActionScript 3.0" - }, - { - "key": "DATE —", - "value": "January 2015" - } - ], "buttons": [ { "name": "Download", - "link": "https://drive.google.com/file/d/14FD_603deu6C40w49c3u3vpvZOD9sDkA/view?usp=drive_link" + "url": "https://drive.google.com/file/d/14FD_603deu6C40w49c3u3vpvZOD9sDkA/view?usp=drive_link", + "icon": "download" }, { "name": "Source Code", - "link": "https://github.com/adamgraham/blackhole" + "url": "https://github.com/adamgraham/blackhole", + "icon": "code" } ], "sections": [ @@ -96,34 +76,24 @@ { "id": "mixed", "category": "art", - "date": "February 2015", "title": "Mixed", "description": "Mixed is an interactive, avant-garde experiment touching on the topics of diversity, racism, and interracial relationships. Navigate around in an abstract space, mixing with different colors to create AI-controlled players. Explore the effects of cultural diversity and see how diversity is (im)balanced in the world. What impact can you leave on the world?", "description_short": "Mixed is an interactive, avant-garde experiment touching on the topics of diversity, racism, and interracial relationships. Navigate around mixing with different colors to create AI-controlled players. Explore the effects of cultural diversity and see how diversity is (im)balanced in the world.", + "date": "February 2015", + "role": "Solo Project", + "tech": ["Adobe Flash", "ActionScript 3.0"], "image": "../images/banners/mixed-painting.jpg", "imageAltText": "Mixed Painting", - "details": [ - { - "key": "ROLE —", - "value": "Solo Project" - }, - { - "key": "TECH —", - "value": "Adobe Flash, ActionScript 3.0" - }, - { - "key": "DATE —", - "value": "February 2015" - } - ], "buttons": [ { "name": "Download", - "link": "https://drive.google.com/file/d/1Sr5wxTQrm2jqemSOTLa0xlzYCEKysixm/view?usp=drive_link" + "url": "https://drive.google.com/file/d/1Sr5wxTQrm2jqemSOTLa0xlzYCEKysixm/view?usp=drive_link", + "icon": "download" }, { "name": "Source Code", - "link": "https://github.com/adamgraham/mixed" + "url": "https://github.com/adamgraham/mixed", + "icon": "code" } ], "sections": [ @@ -142,33 +112,23 @@ { "id": "hexahedroniks", "category": "art", - "date": "June 2015", "title": "Hexahedroniks", "description": "Hexahedroniks is a virtual reality, puzzle-like toy inspired by Rubik cubes. Paint the cube various colors by looking around and shooting colored orbs at the many faces. Avoid turning the cube all black while enjoying the calming experience of this art piece.", + "date": "June 2015", + "role": "Solo Project", + "tech": ["Unity", "C#", "Oculus Rift"], "image": "../images/banners/hexahedroniks-painting.jpg", "imageAltText": "Hexahedroniks Painting", - "details": [ - { - "key": "ROLE —", - "value": "Solo Project" - }, - { - "key": "TECH —", - "value": "Unity, C#, Oculus Rift" - }, - { - "key": "DATE —", - "value": "June 2015" - } - ], "buttons": [ { "name": "Download", - "link": "https://drive.google.com/drive/folders/1uJo0g6eBtnyHLSCCmUatIQl80VX0UY_9?usp=drive_link" + "url": "https://drive.google.com/drive/folders/1uJo0g6eBtnyHLSCCmUatIQl80VX0UY_9?usp=drive_link", + "icon": "download" }, { "name": "Source Code", - "link": "https://github.com/adamgraham/hexahedroniks" + "url": "https://github.com/adamgraham/hexahedroniks", + "icon": "code" } ], "sections": [ diff --git a/src/data/games.json b/src/data/games.json index caa5130..834e949 100644 --- a/src/data/games.json +++ b/src/data/games.json @@ -2,24 +2,19 @@ { "id": "the-wandering-dark", "category": "games", - "date": "June 2015", "title": "The Wandering Dark", "description": "The Wandering Dark is an indie adventure game that takes you through a girl's dreams and nightmares. Explore her unique dream worlds, conquer her fears, and fight for survival as she matures and grows. Adam developed the game alongside Michael Louden for his senior capstone project before graduating from DePaul University.", "description_short": "The Wandering Dark is an indie adventure game that takes you through a girl's dreams and nightmares. Explore her unique dream worlds, conquer her fears, and fight for survival as she matures and grows.", + "date": "June 2015", + "role": "Lead Programmer, Co-Designer", + "tech": ["Unity", "C#"], "image": "../images/banners/the-wandering-dark-painting.jpg", "imageAltText": "The Wandering Dark Painting", - "details": [ - { - "key": "ROLE —", - "value": "Lead Programmer, Co-Designer" - }, - { - "key": "TECH —", - "value": "Unity, C#" - }, + "buttons": [ { - "key": "DATE —", - "value": "June 2015" + "name": "Download", + "url": "https://drive.google.com/file/d/1A28Dm-oLWkOgr3L49f46jjcyovBnfp-G/view?usp=drive_link", + "icon": "download" } ], "sections": [ @@ -53,33 +48,23 @@ { "id": "boss-rush", "category": "games", - "date": "November 2014", "title": "Boss Rush", "description": "Boss Rush is a top-down twin-stick shooter consisting of nothing but boss fights. Developed as a university project, the objective was to create a vertical slice of a complete game, thus it consists of one action-packed boss fight – the Spider Tank.", + "date": "November 2014", + "role": "Gameplay & Systems Programmer", + "tech": ["Unity", "C#"], "image": "../images/banners/boss-rush-painting.jpg", "imageAltText": "Boss Rush Painting", - "details": [ - { - "key": "ROLE —", - "value": "Gameplay & Systems Programmer" - }, - { - "key": "TECH —", - "value": "Unity, C#" - }, - { - "key": "DATE —", - "value": "November 2014" - } - ], "buttons": [ { "name": "Download", - "link": "https://github.com/ModSquadWorkshop/BossRush/releases" + "url": "https://github.com/ModSquadWorkshop/BossRush/releases", + "icon": "download" }, { "name": "Source Code", - "link": "https://github.com/adamgraham/boss-rush" + "url": "https://github.com/adamgraham/boss-rush", + "icon": "code" } ], "sections": [ @@ -98,29 +83,18 @@ { "id": "alphas", "category": "games", - "date": "June 2013", "title": "Alphas", "description": "Alphas is a top-down twin-stick shooter developed as a university project. The game consists of five interlinked deadly arenas, several unique weapons, and a multitude of engaging enemies lead by the alpha boss. Can you survive the swarm?", + "date": "June 2013", + "role": "Team Lead, Lead Programmer & Designer", + "tech": ["XNA", "C#"], "image": "../images/banners/alphas-painting.jpg", "imageAltText": "Alphas Painting", - "details": [ - { - "key": "ROLE —", - "value": "Team Lead, Lead Programmer & Designer" - }, - { - "key": "TECH —", - "value": "XNA, C#" - }, - { - "key": "DATE —", - "value": "June 2013" - } - ], "buttons": [ { "name": "Download", - "link": "https://drive.google.com/file/d/1wfnwkmW56U3aYZ4cnrMFPZOIpwjYz-52/view?usp=drive_link" + "url": "https://drive.google.com/file/d/1wfnwkmW56U3aYZ4cnrMFPZOIpwjYz-52/view?usp=drive_link", + "icon": "download" } ], "sections": [ @@ -139,31 +113,22 @@ { "id": "hackathon-for-wildlife", "category": "games", - "date": "November 2015", "title": "Hackathon for Wildlife", "description_short": "An empathetic and educational experience that teaches players about the dangers of poaching. By putting the player in the role of the elephant, it creates a deeper connection between the human and the animal.", "description_long": [ "Developed in 24 hours during the Hackathon for Wildlife, the game is an empathetic and educational experience that teaches players about the dangers of poaching. By putting the player in the role of the elephant, it creates a deeper connection between the human and the animal.", "\"On November 14th and 15th over 60 people came together to explore and develop innovative ways to engage worldwide audiences with wild animals. The hackathon teams created amazing concepts through a combination of technologies that include GPS hardware, data, games, and social media to create a stronger, more vested connection between people and animals.\"" ], + "date": "November 2015", + "role": "Lead Programmer & Designer", + "tech": ["Unity", "C#"], "image": "../images/banners/hackathon-for-wildlife-painting.jpg", "imageAltText": "Hackathon for Wildlife Painting", - "details": [ - { - "key": "AWARDS —", - "value": "1st place \"Best Prototype\", 2nd place \"Most Innovative Use of Technology\"" - }, - { - "key": "ROLE —", - "value": "Team Lead, Lead Programmer & Designer" - }, - { - "key": "TECH —", - "value": "Unity, C#" - }, + "buttons": [ { - "key": "DATE —", - "value": "November 2015" + "name": "Source Code", + "url": "https://github.com/adamgraham/hackathon-for-wildlife", + "icon": "code" } ], "sections": [ @@ -182,26 +147,14 @@ { "id": "ferro", "category": "games", - "date": "April 2015", "title": "Ferro", "description": "Inspired by ferrofluids, Ferro is a virtual reality experience created by Michael Louden and Adam Graham. As a scientist who has discovered a potential gateway into another universe, your job is to channel it to the right frequency. Players use a midi keyboard as the primary input device to initiate the machines and tune the portal.", "description_short": "Ferro is a virtual reality experience inspired by ferrofluids. As a scientist who has discovered a potential gateway into another universe, your job is to channel it to the right frequency using a midi keyboard as the primary input device.", + "date": "April 2015", + "role": "I/O Programmer", + "tech": ["Unity", "C#", "Oculus Rift", "Midi Keyboard"], "image": "../images/banners/ferro-painting.jpg", "imageAltText": "Ferro Painting", - "details": [ - { - "key": "ROLE —", - "value": "I/O Programmer" - }, - { - "key": "TECH —", - "value": "Unity, C#, Oculus Rift, Midi Keyboard" - }, - { - "key": "DATE —", - "value": "April 2015" - } - ], "sections": [ { "title": "Media", @@ -218,34 +171,24 @@ { "id": "elegy", "category": "games", - "date": "March 2015", "title": "Elegy", "description": "Elegy is a deep game covering the five stages of grief as a parent deals with the loss of their child. The parent attends the funeral of their child and tries to rewind time to stop their child's death. The player engages in different scenes to figure out how they can stop the events from happening, but they must ultimately accept the death of their child.", "description_short": "Elegy is a deep game covering the five stages of grief as a parent deals with the loss of their child. While attending the funeral, they try to rewind time to stop the tragedy but they must ultimatley accept the death of their child.", + "date": "March 2015", + "role": "Lead Programmer, Designer", + "tech": ["Adobe Flash", "ActionScript 3.0"], "image": "../images/banners/elegy-painting.jpg", "imageAltText": "Elegy Painting", - "details": [ - { - "key": "ROLE —", - "value": "Lead Programmer, Designer" - }, - { - "key": "TECH —", - "value": "Adobe Flash, ActionScript 3.0" - }, - { - "key": "DATE —", - "value": "March 2015" - } - ], "buttons": [ { "name": "Download", - "link": "https://drive.google.com/file/d/1VOryTrKiH_2HlJKeO_CmzNbVjCrU7wRr/view?usp=drive_link" + "url": "https://drive.google.com/file/d/1VOryTrKiH_2HlJKeO_CmzNbVjCrU7wRr/view?usp=drive_link", + "icon": "download" }, { "name": "Source Code", - "link": "https://github.com/adamgraham/elegy" + "url": "https://github.com/adamgraham/elegy", + "icon": "code" } ], "sections": [ @@ -264,32 +207,14 @@ { "id": "squish-em", "category": "games", - "date": "April 2015", "title": "Squish-em!", "description": "Squish-em! is a simple whack-a-mole re-creation designed for mobile devices. Squish your way to the top of the leaderboards, earn achievements along the way, and brag to your friends when you beat their best score! Do you have what it takes to become the best squisher around?", "description_short": "Squish-em! is a simple whack-a-mole re-creation designed for mobile devices. Squish your way to the top of the leaderboards, earn achievements along the way, and brag to your friends when you beat their best score!", + "date": "April 2015", + "role": "Lead Programmer, Producer, Designer", + "tech": ["Adobe Flash", "ActionScript 3.0", "iOS"], "image": "../images/banners/squishem-game-painting.jpg", "imageAltText": "Squish-em! Painting", - "details": [ - { - "key": "ROLE —", - "value": "Lead Programmer, Producer, Designer" - }, - { - "key": "TECH —", - "value": "Adobe Flash, ActionScript 3.0" - }, - { - "key": "DATE —", - "value": "April 2015" - } - ], - "buttons": [ - { - "name": "Play Game", - "link": "https://squishem.com/" - } - ], "sections": [ { "title": "Media", @@ -306,33 +231,18 @@ { "id": "ancient-odyssey", "category": "games", - "date": "November 2013", "title": "Ancient Odyssey", "description": "Ancient Odyssey is an \"escape\" puzzle game for mobile devices. Each level is individually designed to make you think harder and harder. Can you survive the journey to the underworld by plundering all 30 levels?", + "date": "November 2013", + "role": "Lead Programmer, Producer, Designer", + "tech": ["Adobe Flash", "ActionScript 3.0"], "image": "../images/banners/ancient-odyssey-painting.jpg", "imageAltText": "Ancient Odyssey Painting", - "details": [ - { - "key": "ROLE —", - "value": "Lead Programmer, Producer, Designer" - }, - { - "key": "TECH —", - "value": "Adobe Flash, ActionScript 3.0" - }, - { - "key": "DATE —", - "value": "November 2013" - } - ], "buttons": [ { "name": "Play Game", - "link": "https://www.newgrounds.com/portal/view/628001" - }, - { - "name": "iOS Download", - "link": "https://apps.apple.com/app/pharaohs-escape/id702877946" + "url": "https://www.newgrounds.com/portal/view/628001", + "icon": "play_circle" } ], "sections": [ @@ -353,33 +263,18 @@ { "id": "lunar-escape", "category": "games", - "date": "September 2013", "title": "Lunar Escape", "description": "Lunar Escape is an \"escape\" puzzle game for mobile devices. Each level is individually designed to make you think harder and harder. Do you have the logical skills needed to transcend all 30 levels?", + "date": "September 2013", + "role": "Lead Programmer, Producer, Designer, Artist", + "tech": ["Adobe Flash", "ActionScript 3.0"], "image": "../images/banners/lunar-escape-painting.jpg", "imageAltText": "Lunar Escape Painting", - "details": [ - { - "key": "ROLE —", - "value": "Lead Programmer, Producer, Designer, Artist" - }, - { - "key": "TECH —", - "value": "Adobe Flash, ActionScript 3.0" - }, - { - "key": "DATE —", - "value": "September 2013" - } - ], "buttons": [ { "name": "Play Game", - "link": "https://www.newgrounds.com/portal/view/625640" - }, - { - "name": "iOS Download", - "link": "https://apps.apple.com/app/escape-to-space/id681843951" + "url": "https://www.newgrounds.com/portal/view/625640", + "icon": "play_circle" } ], "sections": [ @@ -400,29 +295,18 @@ { "id": "the-rise", "category": "games", - "date": "July 2013", "title": "The Rise", "description": "The Rise is an \"escape\" puzzle game for mobile devices. Each level is individually designed to make you think harder and harder. Put your logical skills to the test, rise to the top, and conquer all 30 levels.", + "date": "July 2013", + "role": "Lead Programmer, Producer, Designer", + "tech": ["Adobe Flash", "ActionScript 3.0"], "image": "../images/banners/the-rise-painting.jpg", "imageAltText": "The Rise Painting", - "details": [ - { - "key": "ROLE —", - "value": "Lead Programmer, Producer, Designer" - }, - { - "key": "TECH —", - "value": "Adobe Flash, ActionScript 3.0" - }, - { - "key": "DATE —", - "value": "July 2013" - } - ], "buttons": [ { "name": "Play Game", - "link": "https://www.newgrounds.com/portal/view/622585" + "url": "https://www.newgrounds.com/portal/view/622585", + "icon": "play_circle" } ], "sections": [ @@ -443,29 +327,18 @@ { "id": "escape-the-estate", "category": "games", - "date": "August 2013", "title": "Escape the Estate", "description": "Escape the Estate is a point-and-click adventure game. Your life is on the line! Kane is at it again, and this time he is angry! Do you have what it takes to prevent his cruel intentions? Put your puzzle-solving skills to the test in the sequel to Escape the Basement!", + "date": "August 2013", + "role": "Lead Programmer, Producer, Designer, Artist", + "tech": ["Adobe Flash", "ActionScript 3.0"], "image": "../images/banners/escape-the-estate-painting.jpg", "imageAltText": "Escape the Estate Painting", - "details": [ - { - "key": "ROLE —", - "value": "Lead Programmer, Producer, Designer, Artist" - }, - { - "key": "TECH —", - "value": "Adobe Flash, ActionScript 3.0" - }, - { - "key": "DATE —", - "value": "August 2013" - } - ], "buttons": [ { "name": "Play Game", - "link": "https://www.newgrounds.com/portal/view/622915" + "url": "https://www.newgrounds.com/portal/view/622915", + "icon": "play_circle" } ], "sections": [ @@ -487,29 +360,18 @@ { "id": "escape-the-basement", "category": "games", - "date": "August 2012", "title": "Escape the Basement", "description": "Escape the Basement is a point-and-click adventure game. You of all people were chosen to die. A cruel man named Kane has locked you in his basement with no thought of letting you free. Can you outwit his evil plan and escape?", + "date": "August 2012", + "role": "Lead Programmer, Producer, Designer", + "tech": ["Adobe Flash", "ActionScript 3.0"], "image": "../images/banners/escape-the-basement-painting.jpg", "imageAltText": "Escape the Basement Painting", - "details": [ - { - "key": "ROLE —", - "value": "Lead Programmer, Producer, Designer" - }, - { - "key": "TECH —", - "value": "Adobe Flash, ActionScript 3.0" - }, - { - "key": "DATE —", - "value": "August 2012" - } - ], "buttons": [ { "name": "Play Game", - "link": "https://www.newgrounds.com/portal/view/601869" + "url": "https://www.newgrounds.com/portal/view/601869", + "icon": "play_circle" } ], "sections": [ diff --git a/src/data/presentations.json b/src/data/presentations.json index 510346e..f3c3af8 100644 --- a/src/data/presentations.json +++ b/src/data/presentations.json @@ -3,25 +3,18 @@ "id": "the-life-of-a-ux-engineer", "category": "presentations", "title": "The Life of a UX Engineer", - "date": "August 2020", "description": "What is a UX Engineer? This specialty role sits on a cross-functional design team and helps facilitate collaboration between designers and engineers. This presentation explores the relationship between designers and engineers and how a UX Engineer uses a particular skillset to solve UX problems. Adam presented this deck to around 20 professionals at one of Kin + Carta's monthly design meetups.", "description_short": "What is a UX Engineer? This specialty role sits on a cross-functional design team and helps facilitate collaboration between designers and engineers. This presentation explores the relationship between designers and engineers and how a UX Engineer uses a particular skillset to solve UX problems.", + "date": "August 2020", + "role": "Designer, Presenter", + "tech": ["React", "JavaScript", "HTML/CSS", "Adobe Illustrator", "Figma"], "image": "../images/banners/the-life-of-a-ux-engineer-painting.jpg", "imageAltText": "The Life of a UX Engineer Painting", - "details": [ - { - "key": "ROLE —", - "value": "Designer, Presenter" - }, - { - "key": "DATE —", - "value": "August 2020" - } - ], "buttons": [ { "name": "View Deck", - "link": "https://drive.google.com/file/d/1mHUDzD7EzfrRR03Z_1WNsduszsIUj4ny/view?usp=drive_link" + "url": "https://drive.google.com/file/d/1mHUDzD7EzfrRR03Z_1WNsduszsIUj4ny/view?usp=drive_link", + "icon": "picture_as_pdf" } ], "sections": [ @@ -43,25 +36,13 @@ "id": "an-animation-story", "category": "presentations", "title": "An Animation Story", - "date": "August 2017", "description": "A love story between a girl named Design and a boy named Animation. Adam created, narrated, and presented this story to a group of 20 developers at Solstice in order to talk about the importance of having a \"design-gineering\" mindset and the value of the relationship between designers and engineers.", "description_short": "A love story between a girl named Design and a boy named Animation. Adam created, narrated, and presented this story to a group of developers in order to talk about the importance of having a \"design-gineering\" mindset and the value of the relationship between designers and engineers.", + "date": "August 2017", + "role": "Designer, Animator, Narrator", + "tech": ["Unity", "C#"], "image": "../images/banners/an-animation-story-painting.jpg", "imageAltText": "An Animation Story Painting", - "details": [ - { - "key": "ROLE —", - "value": "Designer, Animator, Narrator" - }, - { - "key": "TECH —", - "value": "Unity 3D, C#" - }, - { - "key": "DATE —", - "value": "August 2017" - } - ], "sections": [ { "mainVideo": "uHQvPFud3-I" @@ -81,32 +62,21 @@ "id": "entering-the-new-reality", "category": "presentations", "title": "Entering the New Reality", - "date": "May 2016", "description_short": "A presentation exploring the technologies of VR, AR, and MR. What are the differences between these technologies? What industries will it disrupt and who will be building these applications? What problems will they solve? These are the questions to be answered.", "description_long": [ "A presentation exploring the technologies of VR, AR, and MR. What are the differences between these technologies? What industries will it disrupt and who will be building these applications? What problems will they solve? These are the questions to be answered.", "Adam presented this deck alongside 3 other Solstice engineers during a monthly Solstice Mobile meetup. There were about 100 people that attended the event and listened to the presentation." ], + "date": "May 2016", + "role": "VR Engineer, Co-Presenter", + "tech": ["Unity", "Oculus Rift", "HTC Vive", "Microsoft HoloLens"], "image": "../images/banners/entering-the-new-reality-painting.jpg", "imageAltText": "Entering the New Reality Painting", - "details": [ - { - "key": "ROLE —", - "value": "VR Engineer, Co-Presenter" - }, - { - "key": "TECH —", - "value": "Oculus Rift, HTC Vive, Microsoft HoloLens" - }, - { - "key": "DATE —", - "value": "May 2016" - } - ], "buttons": [ { "name": "View Deck", - "link": "https://drive.google.com/file/d/1nw_EMTZB47svcDjgCPaSAAPE84gNYqfK/view?usp=drive_link" + "url": "https://drive.google.com/file/d/1nw_EMTZB47svcDjgCPaSAAPE84gNYqfK/view?usp=drive_link", + "icon": "picture_as_pdf" } ], "sections": [ @@ -136,32 +106,21 @@ "id": "3d-application-development-overview", "category": "presentations", "title": "3D Application Development", - "date": "April 2016", "description_short": "A presentation to teach introductory concepts for 3D application development covering topics across art, design, and tech. The presentation serves as a starting point from which creators can continue to grow their knowledge.", "description_long": [ "A presentation to teach introductory concepts for 3D application development covering topics across art, design, and tech. The presentation serves as a starting point from which creators can continue to grow their knowledge by providing high-level, easy-to-understand definitions that give a taste of the information they might need to know when developing a 3D application.", "The deck is grounded in the Unity engine to provide real, applicable examples of the concepts. Adam presented this deck to around 30 software professionals who were interested in learning more." ], + "date": "April 2016", + "role": "Designer, Presenter", + "tech": ["Unity", "C#", "Blender"], "image": "../images/banners/3d-application-development-overview-painting.jpg", "imageAltText": "3D Application Development Painting", - "details": [ - { - "key": "ROLE —", - "value": "Designer, Presenter" - }, - { - "key": "TECH —", - "value": "Unity 3D" - }, - { - "key": "DATE —", - "value": "April 2016" - } - ], "buttons": [ { "name": "View Deck", - "link": "https://drive.google.com/file/d/1r2DCuz7PckbaD0CH4NWlKIK1X5-po9MI/view?usp=drive_link" + "url": "https://drive.google.com/file/d/1r2DCuz7PckbaD0CH4NWlKIK1X5-po9MI/view?usp=drive_link", + "icon": "picture_as_pdf" } ], "sections": [ diff --git a/src/data/projects.json b/src/data/projects.json new file mode 100644 index 0000000..70c773d --- /dev/null +++ b/src/data/projects.json @@ -0,0 +1,363 @@ +[ + { + "title": "Client Work", + "projects": [ + { + "title": "3D Farming World", + "description": "Fully simulated 3D world to visualize farming data", + "date": "2024", + "externalLink": "https://www.linkedin.com/in/adamzigurous", + "tags": ["Unity", "React", "3D"] + }, + { + "title": "Wealth Management App", + "description": "Native iPad app + React web app to manage wealth portfolios", + "date": "2021", + "externalLink": "https://www.linkedin.com/in/adamzigurous", + "tags": ["React", "iOS"] + }, + { + "title": "Bale Mobile App", + "description": "Native iOS app to help farmers improve efficiency when baling hay", + "date": "2019", + "externalLink": "https://www.linkedin.com/in/adamzigurous", + "tags": ["iOS"] + }, + { + "title": "Predictive Yield", + "description": "React web app to visualize predictive yield and moisture data", + "date": "2019", + "externalLink": "https://www.linkedin.com/in/adamzigurous", + "tags": ["React"] + }, + { + "title": "Banking / Credit Card App", + "description": "Native iOS app with over 1 million daily active users", + "date": "2018", + "externalLink": "https://www.linkedin.com/in/adamzigurous", + "tags": ["iOS", "React Native"] + }, + { + "title": "Cinematic VR App", + "description": "Mobile VR app that puts the user in the front-seat of a self-driving car", + "date": "2017", + "externalLink": "https://www.linkedin.com/in/adamzigurous", + "tags": ["Unity", "iOS", "VR/AR"] + } + ] + }, + { + "title": "Tech Showcases", + "projects": [ + { + "title": "Blockchain Gardens", + "description": "Real-time 3D visualization of a private Ethereum blockchain instance", + "date": "2018", + "link": "/tech/blockchain-gardens", + "tags": ["Unity", "Blockchain", "3D"] + }, + { + "title": "RockstAR", + "description": "Interactive AR experience of a live rock concert using Microsoft HoloLens", + "date": "2016", + "link": "/tech/rockstar", + "tags": ["Unity", "VR/AR", "3D"] + }, + { + "title": "Vender VR", + "description": "VR experience synced with an IoT vending machine to dispense real products", + "date": "2015", + "link": "/tech/vender-vr", + "tags": ["Unity", "VR/AR", "3D"] + } + ] + }, + { + "title": "Libraries & Frameworks", + "projects": [ + { + "title": "Palette", + "description": "Swift command line tool to parse and generate color palettes", + "date": "2019", + "externalLink": "https://github.com/adamgraham/palette", + "tags": ["iOS"] + }, + { + "title": "Colorly", + "description": "iOS framework with hundreds of functions to manipulate colors", + "date": "2019", + "externalLink": "https://github.com/adamgraham/colorly-ios", + "tags": ["iOS"] + }, + { + "title": "Polykai", + "description": "Dark color scheme for code editors inspired by the Monokai theme", + "date": "2019", + "externalLink": "https://github.com/adamgraham/polykai", + "tags": [] + }, + { + "title": "STween", + "description": "iOS animation framework for creating complex tweening animations", + "date": "2016", + "externalLink": "https://github.com/adamgraham/STween", + "tags": ["iOS"] + }, + { + "title": "Chronos", + "description": "iOS utility framework to create different types of timers", + "date": "2016", + "externalLink": "https://github.com/adamgraham/chronos", + "tags": ["iOS"] + } + ] + }, + { + "title": "Presentations", + "projects": [ + { + "title": "The Life of a UX Engineer", + "description": "Bridging the gap between design and engineering", + "date": "2020", + "link": "/presentations/the-life-of-a-ux-engineer", + "tags": ["React"] + }, + { + "title": "An Animation Story", + "description": "A 3D animated story about designers and engineers", + "date": "2017", + "link": "/presentations/an-animation-story", + "tags": ["Unity", "3D"] + }, + { + "title": "Entering the New Reality", + "description": "Presentation and meet-up exploring VR, AR, and MR", + "date": "2016", + "link": "/presentations/entering-the-new-reality", + "tags": ["VR/AR", "3D"] + }, + { + "title": "3D Application Development", + "description": "Introductory concepts for 3D application development", + "date": "2016", + "link": "/presentations/3d-application-development-overview", + "tags": ["Unity", "3D"] + } + ] + }, + { + "title": "Games", + "projects": [ + { + "title": "Hackathon for Wildlife", + "description": "Empathetic game that teaches about the dangers of poaching", + "date": "2015", + "link": "/games/hackathon-for-wildlife", + "tags": ["Unity", "3D"] + }, + { + "title": "The Wandering Dark", + "description": "Indie adventure game that explores a girl's dreams and nightmares", + "date": "2015", + "link": "/games/the-wandering-dark", + "tags": ["Unity", "3D"] + }, + { + "title": "Squish-em!", + "description": "Whack-a-mole arcade game published on the App Store", + "date": "2015", + "link": "/games/squish-em", + "tags": ["Flash", "iOS", "2D"] + }, + { + "title": "Ferro", + "description": "Virtual reality experience inspired by ferrofluids", + "date": "2015", + "link": "/games/ferro", + "tags": ["Unity", "3D"] + }, + { + "title": "Elegy", + "description": "Deep game covering stages of grief as a parent deals with the loss of their child", + "date": "2015", + "link": "/games/elegy", + "tags": ["Flash", "2D"] + }, + { + "title": "Boss Rush", + "description": "Top-down twin-stick shooter consisting of nothing but boss fights", + "date": "2014", + "link": "/games/boss-rush", + "tags": ["Unity", "3D"] + }, + { + "title": "Alphas", + "description": "Top-down twin-stick shooter consisting of five interlinked deadly arenas", + "date": "2013", + "link": "/games/alphas", + "tags": ["XNA", "3D"] + }, + { + "title": "Ancient Odyssey", + "description": "Puzzle escape room game for iOS and Android", + "date": "2013", + "link": "/games/ancient-odyssey", + "tags": ["Flash", "iOS", "2D"] + }, + { + "title": "Lunar Escape", + "description": "Puzzle escape room game for iOS and Android", + "date": "2013", + "link": "/games/lunar-escape", + "tags": ["Flash", "iOS", "2D"] + }, + { + "title": "The Rise", + "description": "Puzzle escape room game for iOS and Android", + "date": "2013", + "link": "/games/the-rise", + "tags": ["Flash", "iOS", "2D"] + }, + { + "title": "Escape the Estate", + "description": "Point-and-click adventure game for the web", + "date": "2013", + "link": "/games/escape-the-estate", + "tags": ["Flash", "2D"] + }, + { + "title": "Escape the Basement", + "description": "Point-and-click adventure game for the web", + "date": "2012", + "link": "/games/escape-the-basement", + "tags": ["Flash", "2D"] + } + ] + }, + { + "title": "Websites", + "projects": [ + { + "title": "Zigurous", + "description": "Game studio website showcasing assets, tutorials, and games", + "date": "2021", + "link": "/websites/zigurous", + "externalLink": "https://zigurous.com/", + "tags": ["React", "Unity"] + }, + { + "title": "Colorly", + "description": "Versatile collection of color tools for designers and developers", + "date": "2020", + "link": "/websites/colorly", + "externalLink": "https://adamgraham.github.io/colorly", + "tags": ["React"] + }, + { + "title": "Polykai", + "description": "Dark color scheme for code editors inspired by the Monokai theme", + "date": "2019", + "link": "/websites/polykai", + "externalLink": "https://adamgraham.github.io/polykai-website/", + "tags": ["React"] + }, + { + "title": "Be Super", + "description": "Social justice movement calling for everyone to be the hero within", + "date": "2016", + "link": "/websites/be-super", + "externalLink": "https://adamgraham.github.io/preview/besuper", + "tags": ["Muse"] + }, + { + "title": "Margaret Baughman", + "description": "Theatre director, choreographer, and empathetic human", + "date": "2016", + "link": "/websites/margaret-baughman", + "externalLink": "https://adamgraham.github.io/preview/margaretbaughman", + "tags": ["Muse"] + }, + { + "title": "Ashantis Jones", + "description": "Event planner creating memorable events through detailed design", + "date": "2016", + "link": "/websites/ashantis-jones", + "externalLink": "https://adamgraham.github.io/preview/ashantisjones", + "tags": ["Muse"] + }, + { + "title": "Allium", + "description": "Home-based floral design studio - fresh faced floral design", + "date": "2015", + "link": "/websites/allium", + "externalLink": "https://adamgraham.github.io/preview/allium", + "tags": ["Muse"] + }, + { + "title": "Squish-em!", + "description": "One-page artistic cover designed to promote the mobile game", + "date": "2015", + "link": "/websites/squish-em", + "externalLink": "https://adamgraham.github.io/preview/squishem", + "tags": ["Muse"] + }, + { + "title": "Let It Beard", + "description": "Animated short film that follows the misadventures of an enigmatic cult", + "date": "2014", + "link": "/websites/let-it-beard", + "externalLink": "https://adamgraham.github.io/preview/letitbeard", + "tags": ["Muse", "Flash"] + }, + { + "title": "DemonTHON", + "description": "Year-long fundraising org culminating in a 24-hour Dance Marathon", + "date": "2014", + "link": "/websites/demonthon", + "externalLink": "https://adamgraham.github.io/preview/demonthon", + "tags": ["Muse"] + }, + { + "title": "Taylor Cochran Music", + "description": "Singer/songwriter from Nashville pursuing his songwriting career", + "date": "2013", + "link": "/websites/taylor-cochran-music", + "externalLink": "https://adamgraham.github.io/preview/taylorcochranmusic", + "tags": ["Muse"] + } + ] + }, + { + "title": "Interactive Art", + "projects": [ + { + "title": "Hexahedroniks", + "description": "Virtual reality puzzle-like toy inspired by Rubik cubes", + "date": "2015", + "link": "/art/hexahedroniks", + "tags": ["Unity", "VR/AR", "3D"] + }, + { + "title": "Canvas", + "description": "Never-ending interactive painting | Learn to let go and design with the flow", + "date": "2015", + "link": "/art/canvas", + "tags": ["Flash", "2D"] + }, + { + "title": "Mixed", + "description": "Interactive avant-garde experiment about diversity and interracial relationships", + "date": "2015", + "link": "/art/mixed", + "tags": ["Flash", "2D"] + }, + { + "title": "Blackhole", + "description": "Free-form avant-garde game to control particles in unique ways", + "date": "2015", + "link": "/art/blackhole", + "tags": ["Flash", "2D"] + } + ] + } +] diff --git a/src/data/tech.json b/src/data/tech.json index f763ffa..3e6c87b 100644 --- a/src/data/tech.json +++ b/src/data/tech.json @@ -2,29 +2,17 @@ { "id": "blockchain-gardens", "category": "tech", - "date": "June 2018", "title": "Blockchain Gardens", - "description_short": "Blockchain Gardens is a real-time 3D visualization of a private Ethereum blockchain instance. It was one of the main displays at Solstice's annual digital innovation summit - Solstice FWD.", + "description_short": "Blockchain Gardens is a real-time 3D visualization of a private Ethereum blockchain instance. It was one of the main displays at Solstice's annual digital innovation summit.", "description_long": [ "The blockchain is as complicated, misunderstood and powerfully compelling as any emerging technology that currently exists. It's no longer sufficient to merely explain the technical nuances and intricacies, the time has come for you to experience them. Built on the Ethereum backbone, Solstice FWD attendees participated in one of the first human blockchain experiments in history. Interactions and exchanges were recorded on the FWD ledger and the \"future of trust in a trustless world\" was on full display.", "Blockchain Gardens is a real-time 3D visualization of a private Ethereum blockchain instance displayed at Solstice's annual digital innovation summit. Attendees at the event were tracked throughout the space by an RFID in their badge. As they visited each event booth, these moments and experiences were recorded on the blockchain. We wanted to explore what it would be like to put humans at the center of this emerging technology." ], + "date": "June 2018", + "role": "Front-end Developer, 3D Designer", + "tech": ["Unity", "C#", "Ethereum", "Firebase", "RFID", "AWS", "PCF"], "image": "../images/banners/blockchain-gardens-painting.jpg", "imageAltText": "Blockchain Gardens Painting", - "details": [ - { - "key": "ROLE —", - "value": "Solo Front-end Developer, 3D Designer" - }, - { - "key": "TECH —", - "value": "Unity, Ethereum, IoT RFID Readers, Google Firebase, PCF, AWS" - }, - { - "key": "DATE —", - "value": "June 2018" - } - ], "sections": [ { "title": "Media", @@ -45,29 +33,17 @@ { "id": "rockstar", "category": "tech", - "date": "September 2016", "title": "RockstAR", "description_short": "RockstAR is an interactive AR experience demoed at Solstice FWD. By coupling the power of Microsoft's HoloLens, Philips Hue Lightbulbs, and a series of stage technologies, users are given the ability to customize a live rock band experience.", "description_long": [ "Augmented reality is making it possible to merge virtual objects into the physical world around us. To explore the possibilities, Adam and fellow Solstice engineers built RockstAR, an interactive AR experience demoed at Solstice FWD. By coupling the power of Microsoft's HoloLens, Philips Hue Lightbulbs, and a series of stage technologies, users are given the ability to customize a live rock band experience.", "You can choose your song and color theme preferences using digital menus. Once you're ready to go, the band plays your song as you set the stage for your audience and let the lights groove to the beat. You might look a little odd pointing to things no one else can see, but the joke's on them - you're the star of the show!" ], + "date": "September 2016", + "role": "Front-end Developer, Experience Designer", + "tech": ["Unity", "C#", "HoloLens", "Raspberry Pi + Node.js", "Phillips Hue Lights", "IoT Devices", "iPad"], "image": "../images/banners/rockstar-painting.jpg", "imageAltText": "RockstAR Painting", - "details": [ - { - "key": "ROLE —", - "value": "Front-end Developer, Experience Designer" - }, - { - "key": "TECH —", - "value": "Unity, HoloLens, Raspberry Pi + Node.js, Phillips Hue Lights, IoT Fog Machine" - }, - { - "key": "DATE —", - "value": "September 2016" - } - ], "sections": [ { "title": "Media", @@ -87,29 +63,17 @@ { "id": "vender-vr", "category": "tech", - "date": "September 2015", "title": "Vender VR", "description_short": "Vender is an IoT connected vending machine that started as an R&D project at Solstice. Less than a month before Solstice's digital innovation summit, Adam expanded Vender to the virtual world, creating the first ever VR vending machine experience.", "description_long": [ "Vender is an IoT connected vending machine that started as an R&D project at Solstice before Adam joined the team. Less than a month before Solstice's digital innovation summit - Solstice FWD - Vender was expanded to the virtual world. Adam took on the project head first and developed the first ever virtual reality vending machine experience.", "A user vends the drink of their choice by putting their hand up to one of the drink taps and waiting for the cup to be filled. Once filled, a service call is made to the Raspberry Pi hooked up to the vending machine, which then vends the user's drink. Drink choices are populated from a Hybris Platform which stores and manages all of Vender's data." ], + "date": "September 2015", + "role": "Unity Developer", + "tech": ["Unity", "C#", "Oculus Rift", "Leap Motion", "Raspberry Pi", "Hybris Platform"], "image": "../images/banners/vender-vr-painting.jpg", "imageAltText": "Vender VR Painting", - "details": [ - { - "key": "ROLE —", - "value": "Solo VR / Unity Developer" - }, - { - "key": "TECH —", - "value": "Unity, C#, Oculus Rift, Leap Motion, Raspberry Pi, Hybris Platform" - }, - { - "key": "DATE —", - "value": "September 2015" - } - ], "sections": [ { "title": "Media", diff --git a/src/data/websites.json b/src/data/websites.json index 8b5d89a..d241294 100644 --- a/src/data/websites.json +++ b/src/data/websites.json @@ -2,32 +2,26 @@ { "id": "zigurous", "category": "websites", - "date": "2012 - Present", "title": "Zigurous", - "description": "Zigurous is an indie game development studio. Adam has managed the website since he founded the game studio in 2012 and has re-designed it year after year. Below you can see different examples and the progression of the website over time.", - "description_short": "Zigurous is an indie game development studio. Adam has managed the website since he founded the game studio in 2012 and has re-designed it year after year.", + "description": "Zigurous is an indie game development studio. Adam has designed and managed the website since he founded the game studio in 2012. Below you can see different examples and the progression of the website over time.", + "description_short": "Zigurous is an indie game development studio. Adam has designed and managed the website since he founded the game studio in 2012. The website has gone through several design iterations over the years.", + "date": "January 2021", + "role": "Web Designer, Developer, Maintainer", + "tech": ["React", "Gatsby", "JavaScript", "HTML/CSS", "Firebase"], "image": "../images/banners/zigurous-painting.jpg", "imageAltText": "Zigurous Painting", - "details": [ - { - "key": "TECH —", - "value": "React, Gatsby, Firebase" - }, - { - "key": "DATE —", - "value": "2012 - Present" - } - ], "buttons": [ { - "name": "View Website", - "link": "https://zigurous.com/" + "name": "Visit Website", + "url": "https://zigurous.com/", + "icon": "preview" } ], "sections": [ { - "title": "2021 - Latest", - "mainImage": "../images/screenshots/zigurous-screenshot-latest-spread.jpg" + "title": "2021 - Current", + "mainImage": "../images/screenshots/zigurous-screenshot-latest-spread.jpg", + "mainImageLink": "https://zigurous.com/" }, { "title": "2017 - Abstract Theme", @@ -47,266 +41,282 @@ } ] }, + { + "id": "colorly", + "category": "websites", + "title": "Colorly", + "description": "Colorly is a versatile collection of color tools for designers and developers encapsulated in one web app - shades, tints, tones, harmonies, contrast, color blindness, gradients, and more!", + "description_short": "Colorly is a versatile collection of color tools for designers and developers encapsulated in one web app - shades, tints, tones, harmonies, contrast, color blindness, gradients, and more!", + "date": "October 2020", + "role": "Web Designer, Developer", + "tech": ["React", "Redux", "JavaScript", "HTML/CSS"], + "image": "../images/banners/colorly-painting.jpg", + "imageAltText": "Colorly Painting", + "buttons": [ + { + "name": "Visit Website", + "url": "https://adamgraham.github.io/colorly/tints-and-shades", + "icon": "preview" + }, + { + "name": "Source Code", + "url": "https://github.com/adamgraham/colorly", + "icon": "code" + } + ], + "sections": [ + { + "mainImage": "../images/screenshots/colorly-screenshot-spread-01.jpg", + "mainImageLink": "https://adamgraham.github.io/colorly/tints-and-shades" + }, + { + "mainImage": "../images/screenshots/colorly-screenshot-spread-02.jpg", + "mainImageLink": "https://adamgraham.github.io/colorly/tints-and-shades" + } + ] + }, + { + "id": "polykai", + "category": "websites", + "title": "Polykai", + "description": "Polykai is a dark color scheme for code editors inspired by Wimer Hazenberg's Monokai theme. The theme is supported in 20+ code editors and terminals, including a custom VS Code extension.", + "description_short": "Polykai is a dark color scheme for code editors inspired by Wimer Hazenberg's Monokai theme. The theme is supported in 20+ code editors and terminals, including a custom VS Code extension.", + "date": "July 2019", + "role": "Web Designer, Developer", + "tech": ["React", "JavaScript", "HTML/CSS", "VSIX"], + "image": "../images/banners/polykai-painting.jpg", + "imageAltText": "Polykai Painting", + "buttons": [ + { + "name": "Visit Website", + "url": "https://adamgraham.github.io/polykai-website/", + "icon": "preview" + }, + { + "name": "Source Code", + "url": "https://github.com/adamgraham/polykai", + "icon": "code" + } + ], + "sections": [ + { + "mainImage": "../images/screenshots/polykai-screenshot-spread.jpg", + "mainImageLink": "https://adamgraham.github.io/polykai-website/" + } + ] + }, { "id": "allium", "category": "websites", - "date": "November 2015", "title": "Allium", "description_short": "Allium is a home-based floral design studio launched in 2013 by Kim Stanke. After years of working in both retail stores and special event design shops, Kim started her own business to flex her creative muscles.", "description_long": [ "Allium is a home-based floral design studio launched in 2013 by Kim Stanke. After years of working in both retail stores and special event design shops, Kim started her own business to flex her creative muscles.", "Adam designed a new website for Allium as an improvement to the old WordPress site. The goal of the new design was to better promote the business and its floral services." ], + "date": "November 2015", + "role": "Web Designer", + "tech": ["Adobe Muse", "Adobe Photoshop"], "image": "../images/banners/allium-painting.jpg", "imageAltText": "Allium Painting", - "details": [ - { - "key": "TECH —", - "value": "Adobe Muse, Adobe Photoshop" - }, - { - "key": "DATE —", - "value": "November 2015" - } - ], "buttons": [ { - "name": "View Website", - "link": "https://adamgraham.github.io/preview/alliumfloral" + "name": "Visit Website", + "url": "https://adamgraham.github.io/preview/allium", + "icon": "preview" } ], "sections": [ { - "mainImage": "../images/screenshots/allium-screenshot-spread.jpg" + "mainImage": "../images/screenshots/allium-screenshot-spread.jpg", + "mainImageLink": "https://adamgraham.github.io/preview/allium" } ] }, { "id": "demonthon", "category": "websites", - "date": "June 2014", "title": "DemonTHON", - "description_short": "DemonTHON is a year-long fundraising organization benefiting Ann & Robert H. Lurie Children's Hospital of Chicago that culminates in a 24-hour Dance Marathon.", + "description_short": "DemonTHON is a year-long fundraising organization benefiting Ann & Robert H. Lurie Children's Hospital of Chicago that culminates in a 24-hour Dance Marathon at DePaul University.", "description_long": [ - "DemonTHON is a year-long fundraising organization benefiting Ann & Robert H. Lurie Children's Hospital of Chicago that culminates in a 24-hour Dance Marathon.", + "DemonTHON is a year-long fundraising organization benefiting Ann & Robert H. Lurie Children's Hospital of Chicago that culminates in a 24-hour Dance Marathon at DePaul University.", "As the Director of Technology, Adam designed and managed the organization's website from June 2014 to May 2016. The site was awarded \"Best Website\" by Children's Miracle Network Hospitals for the 2014-15 Dance Marathon year." ], + "date": "June 2014", + "role": "Director of Technology", + "tech": ["Adobe Muse", "Adobe Photoshop"], "image": "../images/banners/demonthon-painting.jpg", "imageAltText": "DemonTHON Painting", - "details": [ - { - "key": "TECH —", - "value": "Adobe Muse, Adobe Photoshop" - }, - { - "key": "DATE —", - "value": "June 2014 - May 2016" - } - ], "buttons": [ { - "name": "View Website", - "link": "https://adamgraham.github.io/preview/demonthon" + "name": "Visit Website", + "url": "https://adamgraham.github.io/preview/demonthon", + "icon": "preview" } ], "sections": [ { - "mainImage": "../images/screenshots/demonthon-screenshot-spread.jpg" + "mainImage": "../images/screenshots/demonthon-screenshot-spread.jpg", + "mainImageLink": "https://adamgraham.github.io/preview/demonthon" } ] }, { "id": "be-super", "category": "websites", - "date": "September 2016", "title": "Be Super", - "description": "Be Super strives to use comic book characters as educational tools to understand social justice issues. A movement calling for everyone to be the hero within. Adam designed Be Super's first website to help promote the cause.", + "description": "Be Super strives to use comic book characters as educational tools to understand social justice issues. A movement calling for everyone to be the hero within. Adam designed Be Super's website to help promote the cause.", "description_short": "Be Super strives to use comic book characters as educational tools to understand social justice issues. A movement calling for everyone to be the hero within.", + "date": "September 2016", + "role": "Web Designer", + "tech": ["Adobe Muse", "Adobe Photoshop"], "image": "../images/banners/be-super-painting.jpg", "imageAltText": "Be Super Painting", - "details": [ - { - "key": "TECH —", - "value": "Adobe Muse, Adobe Photoshop" - }, - { - "key": "DATE —", - "value": "September 2016" - } - ], "buttons": [ { - "name": "View Website", - "link": "https://adamgraham.github.io/preview/besuper" + "name": "Visit Website", + "url": "https://adamgraham.github.io/preview/besuper", + "icon": "preview" } ], "sections": [ { - "mainImage": "../images/screenshots/be-super-screenshot-spread.jpg" + "mainImage": "../images/screenshots/be-super-screenshot-spread.jpg", + "mainImageLink": "https://adamgraham.github.io/preview/besuper" } ] }, { "id": "ashantis-jones", "category": "websites", - "date": "June 2016", "title": "Ashantis Jones", - "description": "Ashantis is an event planner from Cleaveland that entertains and creates memorable events through detail oriented design and calculated execution. Adam designed Ashantis's portfolio to assist in her job hunt as a graduate of the BFA Theatre Management at DePaul University looking for opportunities in the event management space.", - "description_short": "Ashantis is an event planner from Cleaveland that entertains and creates memorable events through detail oriented design and calculated execution.", + "description": "Ashantis Jones is an event planner from Cleaveland, Ohio that entertains and creates memorable events through detail oriented design and calculated execution. Adam designed Ashantis's portfolio to assist in her job hunt as a graduate of the BFA Theatre Management at DePaul University looking for opportunities in the event management space.", + "description_short": "Ashantis Jones is an event planner from Cleaveland, Ohio that entertains and creates memorable events through detail oriented design and calculated execution.", + "date": "June 2016", + "role": "Web Designer", + "tech": ["Adobe Muse", "Adobe Photoshop"], "image": "../images/banners/ashantis-jones-painting.jpg", "imageAltText": "Ashantis Jones Painting", - "details": [ - { - "key": "TECH —", - "value": "Adobe Muse, Adobe Photoshop" - }, - { - "key": "DATE —", - "value": "June 2016" - } - ], "buttons": [ { - "name": "View Website", - "link": "https://adamgraham.github.io/preview/ashantisjones" + "name": "Visit Website", + "url": "https://adamgraham.github.io/preview/ashantisjones", + "icon": "preview" } ], "sections": [ { - "mainImage": "../images/screenshots/ashantis-jones-screenshot-spread.jpg" + "mainImage": "../images/screenshots/ashantis-jones-screenshot-spread.jpg", + "mainImageLink": "https://adamgraham.github.io/preview/ashantisjones" } ] }, { "id": "margaret-baughman", "category": "websites", - "date": "July 2016", "title": "Margaret Baughman", "description": "Director, choreographer, and empathetic human, Margaret Baughman is enchanted by a world of contradictions that allows her to shake up perspectives through theatre, advocacy, and impeccable organization. Adam designed Margaret's portfolio to help showcase her work.", "description_short": "Director, choreographer, and empathetic human, Margaret Baughman is enchanted by a world of contradictions that allows her to shake up perspectives through theatre, advocacy, and impeccable organization.", + "date": "July 2016", + "role": "Web Designer", + "tech": ["Adobe Muse", "Adobe Photoshop"], "image": "../images/banners/margaret-baughman-painting.jpg", "imageAltText": "Margaret Baughman Painting", - "details": [ - { - "key": "TECH —", - "value": "Adobe Muse, Adobe Photoshop" - }, - { - "key": "DATE —", - "value": "July 2016" - } - ], "buttons": [ { - "name": "View Website", - "link": "https://adamgraham.github.io/preview/margaretbaughman" + "name": "Visit Website", + "url": "https://adamgraham.github.io/preview/margaretbaughman", + "icon": "preview" } ], "sections": [ { - "mainImage": "../images/screenshots/margaret-baughman-screenshot-spread.jpg" + "mainImage": "../images/screenshots/margaret-baughman-screenshot-spread.jpg", + "mainImageLink": "https://adamgraham.github.io/preview/margaretbaughman" } ] }, { "id": "taylor-cochran-music", "category": "websites", - "date": "December 2013", "title": "Taylor Cochran Music", "description_short": "Taylor Cochran is a singer/songwriter from Nashville, Tennessee who started pursuing his songwriting career in early 2013. Through his music, he aspires to send a message of joy, hope, and love.", "description_long": [ "Taylor Cochran is a singer/songwriter from Nashville, Tennessee who pursued his songwriting career in early 2013 by gaining experience playing in local bars, venues, and with his church. Through his music, he aspires to send a message of joy, hope, and love.", - "Adam designed and managed Taylor’s website from late 2013 to mid 2014. The website was launched alongside the success of Taylor's debut EP Kickstarter campaign for which he also helped produce the promo video." + "Adam designed and managed Taylor's website from late 2013 to mid 2014. The website was launched alongside the success of Taylor's debut EP Kickstarter campaign for which he also helped produce the promo video." ], + "date": "December 2013", + "role": "Web Designer", + "tech": ["Adobe Muse", "Adobe Photoshop"], "image": "../images/banners/taylor-cochran-music-painting.jpg", "imageAltText": "Taylor Cochran Music Painting", - "details": [ - { - "key": "TECH —", - "value": "Adobe Muse, Adobe Photoshop" - }, - { - "key": "DATE —", - "value": "December 2013" - } - ], "buttons": [ { - "name": "View Website", - "link": "https://adamgraham.github.io/preview/taylorcochranmusic" + "name": "Visit Website", + "url": "https://adamgraham.github.io/preview/taylorcochranmusic", + "icon": "preview" } ], "sections": [ { - "mainImage": "../images/screenshots/taylor-cochran-music-screenshot-spread.jpg" + "mainImage": "../images/screenshots/taylor-cochran-music-screenshot-spread.jpg", + "mainImageLink": "https://adamgraham.github.io/preview/taylorcochranmusic" } ] }, { - "id": "squishem", + "id": "squish-em", "category": "websites", - "date": "April 2015", "title": "Squish-em!", "description_short": "Squish-em! is a simple whack-a-mole re-creation designed for mobile devices. The website is a one-page, artistic cover designed to promote the game and direct users to download the mobile app.", "description_long": [ "Squish-em! is a simple whack-a-mole re-creation designed for mobile devices. Squish your way to the top of the leaderboards, earn achievements along the way, and brag to your friends when you beat their best score!", "The Squish-em! website is a one-page, artistic cover designed to promote the game and direct users to download the mobile app." ], + "date": "April 2015", + "role": "Web Designer", + "tech": ["Adobe Muse", "Adobe Photoshop", "Adobe Flash"], "image": "../images/banners/squishem-website-painting.jpg", "imageAltText": "Squish-em! Painting", - "details": [ - { - "key": "TECH —", - "value": "Adobe Muse, Adobe Photoshop, Adobe Flash" - }, - { - "key": "DATE —", - "value": "April 2015" - } - ], "buttons": [ { - "name": "View Website", - "link": "https://adamgraham.github.io/preview/squishem" + "name": "Visit Website", + "url": "https://adamgraham.github.io/preview/squishem", + "icon": "preview" } ], "sections": [ { - "mainImage": "../images/screenshots/squishem-website-screenshot-spread.jpg" + "mainImage": "../images/screenshots/squishem-website-screenshot-spread.jpg", + "mainImageLink": "https://adamgraham.github.io/preview/squishem" } ] }, { "id": "let-it-beard", "category": "websites", - "date": "November 2014", "title": "Let It Beard", "description_short": "Let It Beard is an animated short film that follows the misadventures of an enigmatic religious family known as \"The Cult\". The Captain and his devoted followers aim for truth, inner peace, and run various endeavors on their path to enlightenment.", "description_long": [ "Let It Beard is an animated short film that follows the misadventures of an enigmatic religious family known simply as \"The Cult\". The Captain and his bevy of devoted followers aim for truth, inner peace, and run various entrepreneurial endeavors on their path to transcendent enlightenment.", "The Let It Beard website is a simple one-page design to help promote the animated short film. A random animated background is displayed upon each visit alongside various idle animations. Visit multiple times to see different designs. Requires Adobe Flash." ], + "date": "November 2014", + "role": "Web Designer", + "tech": ["Adobe Muse", "Adobe Photoshop", "Adobe Flash"], "image": "../images/banners/let-it-beard-painting.jpg", "imageAltText": "Let It Beard Painting", - "details": [ - { - "key": "TECH —", - "value": "Adobe Muse, Adobe Photoshop, Adobe Flash" - }, - { - "key": "DATE —", - "value": "November 2014" - } - ], "buttons": [ { - "name": "View Website", - "link": "https://adamgraham.github.io/preview/letitbeard" + "name": "Visit Website", + "url": "https://adamgraham.github.io/preview/letitbeard", + "icon": "preview" } ], "sections": [ { - "mainImage": "../images/screenshots/let-it-beard-screenshot.jpg" + "mainImage": "../images/screenshots/let-it-beard-screenshot.jpg", + "mainImageLink": "https://adamgraham.github.io/preview/letitbeard" } ] } diff --git a/src/images/banners/colorly-painting.jpg b/src/images/banners/colorly-painting.jpg new file mode 100644 index 0000000..30ea448 Binary files /dev/null and b/src/images/banners/colorly-painting.jpg differ diff --git a/src/images/banners/polykai-painting.jpg b/src/images/banners/polykai-painting.jpg new file mode 100644 index 0000000..a0c2f23 Binary files /dev/null and b/src/images/banners/polykai-painting.jpg differ diff --git a/src/images/screenshots/colorly-screenshot-spread-01.jpg b/src/images/screenshots/colorly-screenshot-spread-01.jpg new file mode 100644 index 0000000..64a2f2a Binary files /dev/null and b/src/images/screenshots/colorly-screenshot-spread-01.jpg differ diff --git a/src/images/screenshots/colorly-screenshot-spread-02.jpg b/src/images/screenshots/colorly-screenshot-spread-02.jpg new file mode 100644 index 0000000..fbef150 Binary files /dev/null and b/src/images/screenshots/colorly-screenshot-spread-02.jpg differ diff --git a/src/images/screenshots/colorly-screenshot-spread-03.jpg b/src/images/screenshots/colorly-screenshot-spread-03.jpg new file mode 100644 index 0000000..285de02 Binary files /dev/null and b/src/images/screenshots/colorly-screenshot-spread-03.jpg differ diff --git a/src/images/screenshots/colorly-screenshot.jpg b/src/images/screenshots/colorly-screenshot.jpg new file mode 100644 index 0000000..1235f56 Binary files /dev/null and b/src/images/screenshots/colorly-screenshot.jpg differ diff --git a/src/images/screenshots/polykai-screenshot-spread.jpg b/src/images/screenshots/polykai-screenshot-spread.jpg new file mode 100644 index 0000000..c8ce07a Binary files /dev/null and b/src/images/screenshots/polykai-screenshot-spread.jpg differ diff --git a/src/images/screenshots/polykai-screenshot.jpg b/src/images/screenshots/polykai-screenshot.jpg new file mode 100644 index 0000000..7b01a23 Binary files /dev/null and b/src/images/screenshots/polykai-screenshot.jpg differ diff --git a/src/links.js b/src/links.js index 2a82ec1..f117bd2 100644 --- a/src/links.js +++ b/src/links.js @@ -6,7 +6,7 @@ export const linkedIn = 'https://www.linkedin.com/in/adamzigurous'; export const instagram = 'http://instagram.com/adam.zigurous'; export const twitter = 'https://twitter.com/zigurous'; -export const navLinks = [ +export const headerLinks = [ { key: 'games', name: 'Games', @@ -19,12 +19,12 @@ export const navLinks = [ }, { key: 'art', - name: 'Visual Art', + name: 'Interactive Art', to: '/art', }, { key: 'tech', - name: 'Emerging Tech', + name: 'Tech Showcases', to: '/tech', }, { @@ -32,13 +32,27 @@ export const navLinks = [ name: 'Presentations', to: '/presentations', }, - // { - // name: 'Software', - // to: github, - // rightIcon: 'open_in_new', - // ElementType: 'a', - // external: true, - // }, +]; + +export const dockLinks = [ + { + key: 'home', + name: 'Home', + icon: 'home', + to: '/', + }, + { + key: 'gallery', + name: 'Gallery', + icon: 'collections', + to: '/games', + }, + { + key: 'projects', + name: 'Projects', + icon: 'menu', + to: '/projects', + }, ]; export const socialLinks = [ @@ -53,18 +67,15 @@ export const socialLinks = [ url: linkedIn, }, { - key: 'instagram', - name: 'Instagram', - url: instagram, + key: 'resume', + name: 'Resume', + icon: 'description', + url: resume, }, - // { - // key: 'twitter', - // name: 'Twitter', - // url: twitter, - // }, { key: 'email', name: 'Email', + icon: 'mail', url: email, }, ]; diff --git a/src/pages/art.js b/src/pages/art.js index c0ac13a..10a0f6c 100644 --- a/src/pages/art.js +++ b/src/pages/art.js @@ -34,15 +34,15 @@ const Art = ({ data, location }) => { const { slides } = data.json; return ( - + ); }; diff --git a/src/pages/art/{ArtJson.jsonId}.js b/src/pages/art/{ArtJson.jsonId}.js index 39d79b8..ee8d112 100644 --- a/src/pages/art/{ArtJson.jsonId}.js +++ b/src/pages/art/{ArtJson.jsonId}.js @@ -1,7 +1,7 @@ import { graphql } from 'gatsby'; import PropTypes from 'prop-types'; import React from 'react'; -import { Page, Project, ProjectProps, SlideProps } from '../../components'; +import { Page, Project, ProjectProps } from '../../components'; import { baseUri } from '../../links'; export const query = graphql` @@ -10,10 +10,12 @@ export const query = graphql` id: jsonId category title - date description description_short description_long + date + role + tech image { sharp: childImageSharp { original { @@ -23,13 +25,9 @@ export const query = graphql` } } } - details { - key - value - } buttons { name - link + url icon } sections { @@ -44,6 +42,7 @@ export const query = graphql` } } } + mainImageLink mainVideo paragraphs gallery { @@ -58,36 +57,15 @@ export const query = graphql` videos } } - json: allArtJson { - slides: nodes { - id - category - title - description - description_short - image { - sharp: childImageSharp { - original { - src - width - height - } - } - } - imageAltText - imageBorder - } - } } `; const Art = ({ data, location }) => { const { project } = data; - const { slides } = data.json; return ( { }; Art.propTypes = { - data: PropTypes.shape({ - project: ProjectProps, - json: PropTypes.shape({ - slides: PropTypes.arrayOf(SlideProps), - }), - }), + data: PropTypes.shape({ project: ProjectProps }), location: PropTypes.object, }; diff --git a/src/pages/games.js b/src/pages/games.js index c15db1a..77a6ccf 100644 --- a/src/pages/games.js +++ b/src/pages/games.js @@ -34,15 +34,15 @@ const Games = ({ data, location }) => { const { slides } = data.json; return ( - + ); }; diff --git a/src/pages/games/{GamesJson.jsonId}.js b/src/pages/games/{GamesJson.jsonId}.js index 4d6ef94..5e97965 100644 --- a/src/pages/games/{GamesJson.jsonId}.js +++ b/src/pages/games/{GamesJson.jsonId}.js @@ -1,7 +1,7 @@ import { graphql } from 'gatsby'; import PropTypes from 'prop-types'; import React from 'react'; -import { Page, Project, ProjectProps, SlideProps } from '../../components'; +import { Page, Project, ProjectProps } from '../../components'; import { baseUri } from '../../links'; export const query = graphql` @@ -10,10 +10,12 @@ export const query = graphql` id: jsonId category title - date description description_short description_long + date + role + tech image { sharp: childImageSharp { original { @@ -23,13 +25,9 @@ export const query = graphql` } } } - details { - key - value - } buttons { name - link + url icon } sections { @@ -44,6 +42,7 @@ export const query = graphql` } } } + mainImageLink mainVideo paragraphs gallery { @@ -58,36 +57,15 @@ export const query = graphql` videos } } - json: allGamesJson { - slides: nodes { - id - category - title - description - description_short - image { - sharp: childImageSharp { - original { - src - width - height - } - } - } - imageAltText - imageBorder - } - } } `; const Game = ({ data, location }) => { const { project } = data; - const { slides } = data.json; return ( { }; Game.propTypes = { - data: PropTypes.shape({ - project: ProjectProps, - json: PropTypes.shape({ - slides: PropTypes.arrayOf(SlideProps), - }), - }), + data: PropTypes.shape({ project: ProjectProps }), location: PropTypes.object, }; diff --git a/src/pages/index.js b/src/pages/index.js index dcf4e9d..04785da 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,21 +1,23 @@ -import { Button, ButtonGroup, Link } from '@zigurous/react-components'; +import { ButtonGroup, Link } from '@zigurous/react-components'; import { Link as GatsbyLink } from 'gatsby'; import React, { useRef } from 'react'; -import { Page } from '../components'; +import { Page, ShadowButton } from '../components'; import { useElementSize } from '../hooks/useElementSize'; -import { baseUri, resume } from '../links'; +import { baseUri } from '../links'; -const Home = () => { +const Home = ({ location }) => { const ref = useRef(); const [_, scale] = useElementSize(ref); return (
{ style={{ transform: `scale(${scale})` }} > Hello! 👋 My name is -

Adam Graham

-

+

+ Adam Graham +

+

I'm a software engineer and game developer inspired by the blending of art, design, and engineering to create best in class user experiences. I love working on design centric projects across web @@ -35,42 +39,19 @@ const Home = () => { - + Gallery - {/* - - */} - - + Projects

diff --git a/src/pages/presentations.js b/src/pages/presentations.js index ed7fd57..88a9301 100644 --- a/src/pages/presentations.js +++ b/src/pages/presentations.js @@ -34,15 +34,15 @@ const Presentations = ({ data, location }) => { const { slides } = data.json; return ( - + ); }; diff --git a/src/pages/presentations/{PresentationsJson.jsonId}.js b/src/pages/presentations/{PresentationsJson.jsonId}.js index 68ab693..dace98f 100644 --- a/src/pages/presentations/{PresentationsJson.jsonId}.js +++ b/src/pages/presentations/{PresentationsJson.jsonId}.js @@ -1,7 +1,7 @@ import { graphql } from 'gatsby'; import PropTypes from 'prop-types'; import React from 'react'; -import { Page, Project, ProjectProps, SlideProps } from '../../components'; +import { Page, Project, ProjectProps } from '../../components'; import { baseUri } from '../../links'; export const query = graphql` @@ -10,10 +10,12 @@ export const query = graphql` id: jsonId category title - date description description_short description_long + date + role + tech image { sharp: childImageSharp { original { @@ -23,13 +25,9 @@ export const query = graphql` } } } - details { - key - value - } buttons { name - link + url icon } sections { @@ -44,6 +42,7 @@ export const query = graphql` } } } + mainImageLink mainVideo paragraphs gallery { @@ -58,36 +57,15 @@ export const query = graphql` videos } } - json: allPresentationsJson { - slides: nodes { - id - category - title - description - description_short - image { - sharp: childImageSharp { - original { - src - width - height - } - } - } - imageAltText - imageBorder - } - } } `; const Presentation = ({ data, location }) => { const { project } = data; - const { slides } = data.json; return ( { }; Presentation.propTypes = { - data: PropTypes.shape({ - project: ProjectProps, - json: PropTypes.shape({ - slides: PropTypes.arrayOf(SlideProps), - }), - }), + data: PropTypes.shape({ project: ProjectProps }), location: PropTypes.object, }; diff --git a/src/pages/projects.js b/src/pages/projects.js new file mode 100644 index 0000000..9e34df6 --- /dev/null +++ b/src/pages/projects.js @@ -0,0 +1,154 @@ +import { Badge, Link } from '@zigurous/react-components'; +import classNames from 'classnames'; +import { graphql, Link as GatsbyLink, navigate } from 'gatsby'; +import PropTypes from 'prop-types'; +import React, { useMemo } from 'react'; +import { Page } from '../components'; +import { baseUri } from '../links'; +import '../styles/projects-list.css'; + +export const query = graphql` + query Projects { + json: allProjectsJson { + categories: nodes { + title + projects { + title + description + date + link + externalLink + tags + } + } + } + } +`; + +const filters = ['React', 'iOS', 'Unity', 'VR/AR', '3D', '2D']; + +const Projects = ({ data, location }) => { + const filter = useMemo(() => { + const urlParams = new URLSearchParams(location?.search); + return urlParams.has('filter') ? urlParams.get('filter') : null; + }, [location]); + + const filteredCategories = useMemo(() => { + if (!filter) return data.json.categories; + return data.json.categories.map((category) => { + const projects = category.projects.map((project) => ({ + ...project, + hidden: !project.tags.includes(filter), + })); + return { + title: category.title, + projects, + empty: projects.every((project) => project.hidden), + }; + }); + }, [data, filter]); + + return ( + +
+
+

Projects

+
+ {filters.map((tag) => ( + + ))} +
+
+ {filteredCategories.map((category) => ( +
+

{category.title}

+
    + {category.projects.map((project) => ( +
  • + +
    + + {project.title} + + + {project.description} + +
    +
    +
    {project.date}
    + +
  • + ))} +
+
+ ))} +
+
+ ); +}; + +Projects.propTypes = { + data: PropTypes.shape({ + json: PropTypes.shape({ + title: PropTypes.string, + Projects: PropTypes.arrayOf( + PropTypes.shape({ + title: PropTypes.string, + description: PropTypes.string, + date: PropTypes.string, + link: PropTypes.string, + externalLink: PropTypes.string, + tags: PropTypes.arrayOf(PropTypes.string), + }) + ), + }), + }), + location: PropTypes.object, +}; + +export default Projects; diff --git a/src/pages/tech.js b/src/pages/tech.js index 710ff11..6e09fd5 100644 --- a/src/pages/tech.js +++ b/src/pages/tech.js @@ -34,15 +34,15 @@ const Tech = ({ data, location }) => { const { slides } = data.json; return ( - + ); }; diff --git a/src/pages/tech/{TechJson.jsonId}.js b/src/pages/tech/{TechJson.jsonId}.js index 6e9e29b..b4e43cd 100644 --- a/src/pages/tech/{TechJson.jsonId}.js +++ b/src/pages/tech/{TechJson.jsonId}.js @@ -1,7 +1,7 @@ import { graphql } from 'gatsby'; import PropTypes from 'prop-types'; import React from 'react'; -import { Page, Project, ProjectProps, SlideProps } from '../../components'; +import { Page, Project, ProjectProps } from '../../components'; import { baseUri } from '../../links'; export const query = graphql` @@ -10,10 +10,12 @@ export const query = graphql` id: jsonId category title - date description description_short description_long + date + role + tech image { sharp: childImageSharp { original { @@ -23,13 +25,9 @@ export const query = graphql` } } } - details { - key - value - } buttons { name - link + url icon } sections { @@ -44,6 +42,7 @@ export const query = graphql` } } } + mainImageLink mainVideo paragraphs gallery { @@ -58,36 +57,15 @@ export const query = graphql` videos } } - json: allTechJson { - slides: nodes { - id - category - title - description - description_short - image { - sharp: childImageSharp { - original { - src - width - height - } - } - } - imageAltText - imageBorder - } - } } `; const Tech = ({ data, location }) => { const { project } = data; - const { slides } = data.json; return ( { }; Tech.propTypes = { - data: PropTypes.shape({ - project: ProjectProps, - json: PropTypes.shape({ - slides: PropTypes.arrayOf(SlideProps), - }), - }), + data: PropTypes.shape({ project: ProjectProps }), location: PropTypes.object, }; diff --git a/src/pages/websites.js b/src/pages/websites.js index 7601322..5949aa5 100644 --- a/src/pages/websites.js +++ b/src/pages/websites.js @@ -34,15 +34,15 @@ const Websites = ({ data, location }) => { const { slides } = data.json; return ( - + ); }; diff --git a/src/pages/websites/{WebsitesJson.jsonId}.js b/src/pages/websites/{WebsitesJson.jsonId}.js index d12f5e6..b7047d9 100644 --- a/src/pages/websites/{WebsitesJson.jsonId}.js +++ b/src/pages/websites/{WebsitesJson.jsonId}.js @@ -1,7 +1,7 @@ import { graphql } from 'gatsby'; import PropTypes from 'prop-types'; import React from 'react'; -import { Page, Project, ProjectProps, SlideProps } from '../../components'; +import { Page, Project, ProjectProps } from '../../components'; import { baseUri } from '../../links'; export const query = graphql` @@ -10,10 +10,12 @@ export const query = graphql` id: jsonId category title - date description description_short description_long + date + role + tech image { sharp: childImageSharp { original { @@ -23,13 +25,9 @@ export const query = graphql` } } } - details { - key - value - } buttons { name - link + url icon } sections { @@ -44,6 +42,7 @@ export const query = graphql` } } } + mainImageLink mainVideo paragraphs gallery { @@ -58,36 +57,15 @@ export const query = graphql` videos } } - json: allWebsitesJson { - slides: nodes { - id - category - title - description - description_short - image { - sharp: childImageSharp { - original { - src - width - height - } - } - } - imageAltText - imageBorder - } - } } `; const Website = ({ data, location }) => { const { project } = data; - const { slides } = data.json; return ( { }; Website.propTypes = { - data: PropTypes.shape({ - project: ProjectProps, - json: PropTypes.shape({ - slides: PropTypes.arrayOf(SlideProps), - }), - }), + data: PropTypes.shape({ project: ProjectProps }), location: PropTypes.object, }; diff --git a/src/styles/dock.css b/src/styles/dock.css new file mode 100644 index 0000000..f6a774d --- /dev/null +++ b/src/styles/dock.css @@ -0,0 +1,132 @@ +.dock { + display: flex; + justify-content: flex-start; + align-items: center; + position: fixed; + bottom: 1.5rem; + left: 50vw; + transform: translateX(-50%); + z-index: 2000; +} + +.dock__container { + display: flex; + justify-content: space-evenly; + align-items: center; + padding: 8px; + margin: 0 6px; + gap: 8px; + background-color: var(--color-surface-0); + border-radius: 29px; + box-shadow: rgba(42, 51, 70, 0.2) 0px 0px 80px; +} + +[data-theme="dark"] .dock__container, +[data-theme="high-contrast"] .dock__container { + background-color: var(--color-surface-2); + box-shadow: none; +} + +.dock__section { + display: flex; + justify-content: space-evenly; + align-items: center; + gap: 8px; +} + +.dock__section#gallery { + display: none; +} + +.dock__section:not(:last-child)::after { + content: ""; + display: block; + width: 1px; + height: 24px; + margin: 0 4px; + background-color: var(--color-border); + pointer-events: none; +} + +.dock__divider { + width: 1px; + height: 24px; + background-color: var(--color-border); +} + +.dock__item { + position: relative; + display: flex; + justify-content: center; + align-items: center; + width: 42px; + height: 42px; + border-radius: 100%; + background-color: rgba(var(--rgb-default), 0.08); + transition: transform 200ms; +} + +.dock__item:hover { + transform: translateY(-4px) scale(1.2); +} + +.dock__item:active { + transform: translateY(-1px) scale(1.1); +} + +.dock__item > a, +.dock__item > button { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + overflow: hidden; +} + +.dock__item > a > i, +.dock__item > button > i { + color: rgba(var(--rgb-default), 0.4); + fill: rgba(var(--rgb-default), 0.4); + font-size: 20px; +} + +.dock__tooltip { + position: absolute; + top: -75%; + left: 50%; + transform: translateX(-50%) scale(0.9); + padding: 3px 8px; + color: var(--color-foreground-muted); + background-color: var(--color-surface-0); + border: 1px solid var(--color-border); + border-radius: 8px; + font-size: 0.625rem; + font-weight: 500; + letter-spacing: 0; + white-space: nowrap; + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: opacity 200ms, visibility 400ms, transform 200ms; +} + +.dock__item:hover .dock__tooltip { + opacity: 1; + visibility: visible; + transform: translateX(-50%) scale(1); +} + +@media (max-width: 576px) { + .dock__section#socials { + display: none; + } + + .page#games + .dock .dock__section#gallery, + .page#websites + .dock .dock__section#gallery, + .page#art + .dock .dock__section#gallery, + .page#tech + .dock .dock__section#gallery, + .page#presentations + .dock .dock__section#gallery { + display: flex; + } +} diff --git a/src/styles/gallery.css b/src/styles/gallery.css index 5252f4a..3beacc1 100644 --- a/src/styles/gallery.css +++ b/src/styles/gallery.css @@ -37,6 +37,10 @@ fill: var(--color-foreground); } +.gallery--vertical .gallery__slides { + margin-bottom: 8rem; +} + .gallery--vertical .slide { flex-direction: column; align-items: flex-start; diff --git a/src/styles/global.css b/src/styles/global.css index 6bd803f..9c25366 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -4,12 +4,12 @@ html { body { overflow-x: hidden; - overflow-y: auto; + overflow-y: scroll; } html, body { font-family: "Inter", ui-sans-serif, system-ui, sans-serif; - letter-spacing: -0.025em; + letter-spacing: -0.0375em; scroll-behavior: smooth; } @@ -19,45 +19,32 @@ html, body { } } -.page#cover .btn .icon { - transform: translateY(1px); -} - -.shadow-button { - color: var(--color-default); - background-color: var(--color-background); - box-shadow: rgba(14, 63, 126, 0.06) 0px 0px 0px 1px, rgba(42, 51, 70, 0.03) 0px 1px 1px -0.5px, rgba(42, 51, 70, 0.04) 0px 2px 2px -1px, rgba(42, 51, 70, 0.04) 0px 3px 3px -1.5px, rgba(42, 51, 70, 0.03) 0px 5px 5px -2.5px, rgba(42, 51, 70, 0.03) 0px 10px 10px -5px, rgba(42, 51, 70, 0.03) 0px 24px 24px -8px; - font-size: 16px; - font-weight: 600; +.page { + overflow-x: hidden; } -.shadow-button:not(:disabled):hover, -.shadow-button:not(:disabled):active, -.shadow-button:not(:disabled).active { - background-color: var(--color-background); +.page > article { + position: relative; + padding-top: 3rem; + padding-bottom: 8rem; } -[data-theme="dark"] .shadow-button, -[data-theme="high-contrast"] .shadow-button { - background-color: var(--color-default-subtle); - box-shadow: none; +.page#cover > .cover { + height: auto; + overflow-y: auto; + min-height: 100vh; } -[data-theme="dark"] .shadow-button:not(:disabled):hover, -[data-theme="dark"] .shadow-button:not(:disabled):active, -[data-theme="dark"] .shadow-button:not(:disabled).active, -[data-theme="high-contrast"] .shadow-button:not(:disabled):hover, -[data-theme="high-contrast"] .shadow-button:not(:disabled):active, -[data-theme="high-contrast"] .shadow-button:not(:disabled).active { - background-color: var(--color-surface-9); +.page#cover .introduction .btn .icon { + transform: translateY(1px); } .ReactModal__Overlay { z-index: 2000 !important; } -.image-gallery__lightbox .ril__outer { - background-color: rgba(255, 255, 255, 0.9); +.embedded-video::before { + background-color: var(--color-surface-1); } .image-gallery__lightbox .ril-image-current.ril__image { diff --git a/src/styles/header.css b/src/styles/header.css index 855d51e..5d0eab7 100644 --- a/src/styles/header.css +++ b/src/styles/header.css @@ -26,34 +26,6 @@ align-items: center; } -.header__container > div { - display: flex; - align-items: center; - flex: none; -} - -.header .logo { - margin: 0; - font-weight: 700; - white-space: nowrap; - transition: color 200ms; -} - -.header .logo > svg { - background-color: var(--color-foreground); - border-radius: 6px; - padding: 2px; -} - -[data-theme="dark"] .header .logo > svg, -[data-theme="high-contrast"] .header .logo > svg { - background-color: var(--color-background); -} - -.header .logo > svg > polygon { - fill: var(--color-white, white); -} - .header .navbar { margin-left: 16px; margin-right: 16px; @@ -61,6 +33,7 @@ .header .navbar li { padding: 0; + margin: 0 3px; } .header .navbar li a { @@ -68,23 +41,32 @@ border: none; font-size: 0.875rem; font-weight: 600; + white-space: nowrap; opacity: var(--opacity-inactive, 0.62); - transition: color 100ms, opacity 100ms; + transition: color 200ms, background-color 200ms, opacity 200ms, transform 200ms; } -.header .navbar li a > .icon { - color: inherit; - transition: color 100ms, opacity 100ms; +.header .navbar li a:hover, +.header .navbar li a.active { + opacity: 1; + background-color: var(--color-default-subtle); } -.header .navbar li a.active, -.header .navbar li a:hover { - opacity: 1; +.header__title-button { + display: none; + color: inherit; + border: none; + padding: 12px; + margin-top: 2px; + font-size: 1.125rem; + font-weight: 700; + letter-spacing: -0.025em; + white-space: nowrap; + transition: background-color .2s, transform .2s; } -.header .navbar li a:hover, -.header .navbar li a.active:hover { - background-color: var(--color-default-subtle); +.header__title-button .icon-wrapper .icon { + font-size: 21px; } .header__menu-button svg { @@ -104,8 +86,12 @@ } } -@media (max-width: 1023px) { +@media (max-width: 819px) { .header .navbar { display: none; } + + .header__title-button { + display: block; + } } diff --git a/src/styles/logo.css b/src/styles/logo.css new file mode 100644 index 0000000..112cb41 --- /dev/null +++ b/src/styles/logo.css @@ -0,0 +1,46 @@ +.ag-logo { + position: relative; +} + +.ag-logo svg { + position: relative; + width: 100%; + height: 100%; + padding: 2px; +} + +.ag-logo svg polygon { + fill: var(--color-white, white); +} + +.ag-logo__background { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: var(--color-default); + border-radius: 6px; + transition: border-radius 600ms ease-in-out, transform 400ms ease-out; +} + +.ag-logo:hover .ag-logo__background { + border-radius: 50%; + transform: rotateZ(90deg) scale(1.05); +} + +.ag-logo--rounded .ag-logo__background { + border-radius: 50%; +} + +[data-theme="dark"] .ag-logo__background, +[data-theme="high-contrast"] .ag-logo__background { + background-color: var(--color-background); +} + +.floating-logo { + position: fixed; + bottom: 1.5rem; + left: 1.5rem; + z-index: 2000; +} diff --git a/src/styles/menu.css b/src/styles/menu-gallery.css similarity index 69% rename from src/styles/menu.css rename to src/styles/menu-gallery.css index 6c43aa7..2a379e3 100644 --- a/src/styles/menu.css +++ b/src/styles/menu-gallery.css @@ -1,4 +1,4 @@ -.menu { +.menu-gallery { position: fixed; top: 0; bottom: 0; @@ -10,40 +10,24 @@ overflow-y: auto; } -.menu.open { +.menu-gallery.open { pointer-events: all; visibility: visible; } -.menu.closed { +.menu-gallery.closed { pointer-events: none; visibility: hidden; } -.menu__container { +.menu-gallery__container { position: relative; top: var(--header-height, 80px); padding-top: 2rem; padding-bottom: 4rem; } -.menu__gallery { - display: grid; - grid-template-columns: repeat(2, 1fr); - column-gap: 0.5rem; - row-gap: 0.5rem; -} - -.menu__gallery-thumbnail { - position: relative; - display: flex; - align-items: center; - height: max-content; - border-radius: 0.25rem; - overflow: hidden; -} - -.menu ul { +.menu-gallery__list { position: relative; margin-block-start: 0; margin-block-end: 0; @@ -51,64 +35,84 @@ margin-inline-end: 0; padding-inline-start: 0; font-weight: 600; - font-size: 1.75rem; + font-size: 2rem; line-height: 1.25; text-align: left; list-style: none; white-space: nowrap; } -.menu ul > li { +.menu-gallery__section > a { + display: flex; + justify-content: flex-start; + align-items: center; + width: 100%; + font-weight: 700; + margin-bottom: 0.25em; + color: inherit; +} + +.menu-gallery__section > a .icon { + font-weight: 600; +} + +.menu-gallery__grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + column-gap: 0.5rem; + row-gap: 0.5rem; margin-bottom: 2rem; } -.menu ul > li > a { - display: block; - width: 100%; - color: inherit; - font-weight: 700; - margin-bottom: 1rem; +.menu-gallery__thumbnail { + position: relative; + display: flex; + align-items: center; + height: max-content; + background-color: var(--color-surface-1); + border-radius: 0.25rem; + overflow: hidden; } @media (min-width: 576px) { - .menu__container { + .menu-gallery__container { padding-left: 32px; padding-right: 32px; } - .menu ul { - font-size: 2rem; + .menu-gallery__list { + font-size: 2.5rem; } - .menu__gallery { + .menu-gallery__grid { grid-template-columns: repeat(3, 1fr); column-gap: 0.75rem; row-gap: 0.75rem; } - .menu__gallery-thumbnail { + .menu-gallery__thumbnail { border-radius: 0.4rem; } } @media (min-width: 1024px) { - .menu ul { - font-size: 2.5rem; + .menu-gallery__list { + font-size: 3rem; } - .menu__gallery { + .menu-gallery__grid { grid-template-columns: repeat(4, 1fr); } } @media (min-width: 1366px) { - .menu__gallery { + .menu-gallery__grid { grid-template-columns: repeat(5, 1fr); } } @media (min-width: 1440px) { - .menu__gallery { + .menu-gallery__grid { grid-template-columns: repeat(6, 1fr); } } diff --git a/src/styles/project.css b/src/styles/project.css index b8aaee4..7a20ed1 100644 --- a/src/styles/project.css +++ b/src/styles/project.css @@ -1,10 +1,54 @@ -.page > .project { - padding-top: 32px; +.project.container-md { + width: 100%; + max-width: 720px; + overflow-x: hidden; } -.project h1.title { +.project__title { + line-height: 1; font-weight: 700; - margin-bottom: .75rem; + text-wrap: balance; + margin-bottom: 0.5rem; +} + +.project__subtitle { + font-weight: 600; + font-size: 0.875rem; + letter-spacing: -0.025em; + text-transform: capitalize; + margin-left: 4px; + margin-bottom: 0.75rem; + color: var(--color-foreground-muted, inherit); +} + +.project__subtitle .separator { + margin: 0 0.5rem; + opacity: var(--opacity-inactive, 0.62); +} + +.project__subtitle + .btn-group { + width: 100%; +} + +.project__description { + margin-top: 1.5rem; +} + +.project__badges { + display: flex; + justify-content: flex-start; + align-items: center; + flex-wrap: wrap; + gap: 0.5rem; +} + +.project__badges .badge { + border: none; + background-color: var(--color-primary-subtle); + color: var(--color-primary); + font-weight: 600; + font-size: 0.75em; + margin: 0; } .project section { @@ -12,22 +56,21 @@ margin-bottom: 2rem; } -.project section > p { - margin-bottom: .5rem; +.project section:first-child { + margin-top: 0; +} + +.project section:last-child { + margin-bottom: 0; } .project section > .embedded-video, -.project section > .progressive-image > img { +.project section > .progressive-image > img, +.project section > a > .progressive-image > img { border-radius: .5rem; box-shadow: rgba(42, 51, 70, 0.2) 0px 0px 40px; } -.project em { - font-style: normal; - letter-spacing: -0.5px; - color: var(--color-foreground-muted); -} - .project img { max-width: 100%; } @@ -52,6 +95,23 @@ box-shadow: rgba(14, 63, 126, 0.06) 0px 0px 0px 1px, rgba(42, 51, 70, 0.03) 0px 1px 1px -0.5px, rgba(42, 51, 70, 0.04) 0px 2px 2px -1px, rgba(42, 51, 70, 0.04) 0px 3px 3px -1.5px, rgba(42, 51, 70, 0.03) 0px 5px 5px -2.5px, rgba(42, 51, 70, 0.03) 0px 10px 10px -5px, rgba(42, 51, 70, 0.03) 0px 24px 24px -8px; } +[data-theme="dark"] .project section > .embedded-video, +[data-theme="dark"] .project section > .progressive-image > img, +[data-theme="dark"] .project section > a > .progressive-image > img, +[data-theme="dark"] .project .image-gallery__thumbnails img, +[data-theme="high-contrast"] .project section > .embedded-video, +[data-theme="high-contrast"] .project section > .progressive-image > img, +[data-theme="high-contrast"] .project section > a > .progressive-image > img, +[data-theme="high-contrast"] .project .image-gallery__thumbnails img { + box-shadow: none; +} + +@media (max-width: 1024px) { + .project { + padding-bottom: 144px; + } +} + @media (max-width: 767px) { .project section { margin-top: 1rem; @@ -59,8 +119,14 @@ } } -@media (max-width: 1024px) { - .project { - padding-bottom: 144px; +@media (max-width: 576px) { + .project__title { + font-size: 3rem; + } +} + +@media (max-width: 480px) { + .project__title { + font-size: 2.5rem; } } diff --git a/src/styles/projects-list.css b/src/styles/projects-list.css new file mode 100644 index 0000000..2e3c047 --- /dev/null +++ b/src/styles/projects-list.css @@ -0,0 +1,148 @@ +.projects-list { + font-size: 0.75rem; + width: 100%; + max-width: 962px; + min-height: 105vh; + overflow-x: hidden; +} + +.projects-list__header { + margin-bottom: 2.5rem; +} + +.projects-list__header > h1 { + font-weight: 700; + margin-bottom: 0.5rem; +} + +.projects-list__filters { + display: flex; + justify-content: flex-start; + align-items: center; + flex-wrap: wrap; + width: 100%; + gap: 8px; +} + +.projects-list__filters .badge { + border: none; + background-color: var(--color-primary-subtle); + color: var(--color-primary); + font-weight: 600; + font-size: 0.875em; +} + +.projects-list__filters .badge:hover, +.projects-list__filters .badge--selected { + background-color: var(--color-primary); + color: var(--color-on-primary); +} + +.projects-list__category { + margin-top: 0; + margin-bottom: 2rem; + transition: margin 400ms, opacity 400ms, visibility 400ms; +} + +.projects-list__category--empty { + margin-top: 0; + margin-bottom: 0; + opacity: 0; + visibility: hidden; + pointer-events: none; +} + +.projects-list__category:last-child { + margin-bottom: 0; +} + +.projects-list__category > h2 { + height: 24px; + font-weight: 700; + margin-bottom: 0; + padding-left: 2px; + transition: height 400ms; +} + +.projects-list__category--empty > h2 { + height: 0; +} + +.projects-list__category > ul { + width: 100%; + margin-block-start: 0.5rem; + margin-block-end: 0.5rem; + margin-inline-start: 0; + margin-inline-end: 0; + padding-inline-start: 0; + list-style: none; + white-space: nowrap; + transition: margin-block-start 400ms, margin-block-end 400ms; +} + +.projects-list__category--empty > ul { + margin-block-start: 0; + margin-block-end: 0; +} + +.projects-list__item { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 48px; + border-radius: 16px; + transition: height 400ms, opacity 400ms, visibility 400ms; +} + +.projects-list__item:hover { + background-color: var(--color-surface-2); +} + +.projects-list__item--hidden { + height: 0; + opacity: 0; + visibility: hidden; + pointer-events: none; +} + +.projects-list__item > a { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + padding: 12px; +} + +.projects-list__text { + flex-grow: 0; + flex-shrink: 0; + min-height: 24px; +} + +.projects-list__title { + font-weight: 600; + margin-right: 8px; +} + +.projects-list__description { + display: none; + color: var(--color-foreground-subtle); +} + +.projects-list__line { + flex: 1 1 100%; + margin: 0 .5rem; +} + +.projects-list__date { + flex-grow: 0; + flex-shrink: 0; + color: var(--color-foreground-subtle); +} + +@media (min-width: 700px) { + .projects-list__description { + display: inline-block; + } +} diff --git a/src/styles/shadow-button.css b/src/styles/shadow-button.css new file mode 100644 index 0000000..077e22b --- /dev/null +++ b/src/styles/shadow-button.css @@ -0,0 +1,37 @@ +.shadow-button { + background-color: var(--color-background); + box-shadow: rgba(14, 63, 126, 0.06) 0px 0px 0px 1px, rgba(42, 51, 70, 0.03) 0px 1px 1px -0.5px, rgba(42, 51, 70, 0.04) 0px 2px 2px -1px, rgba(42, 51, 70, 0.04) 0px 3px 3px -1.5px, rgba(42, 51, 70, 0.03) 0px 5px 5px -2.5px, rgba(42, 51, 70, 0.03) 0px 10px 10px -5px, rgba(42, 51, 70, 0.03) 0px 24px 24px -8px; + color: var(--color-default); + font-size: 16px; + font-weight: 600; + transform: scale(1); + transition: transform 200ms, box-shadow 200ms; +} + +.shadow-button:not(:disabled):hover { + transform: translateY(-2px); + box-shadow: rgba(14, 63, 126, 0.06) 0px 0px 0px 1px, rgba(42, 51, 70, 0.03) 0px 1px 1px -0.5px, rgba(42, 51, 70, 0.04) 0px 2px 2px -1px, rgba(42, 51, 70, 0.04) 0px 3px 3px -1.5px, rgba(42, 51, 70, 0.03) 0px 5px 5px -2.5px, rgba(42, 51, 70, 0.03) 0px 10px 10px -5px, rgba(42, 51, 70, 0.03) 0px 24px 24px -8px, rgba(42, 51, 70, 0.06) 0px 4px 10px; +} + +.shadow-button:not(.btn--unstyled):not(:disabled):active { + transform: translateY(-2px) scale(1.04); +} + +.shadow-button:not(:disabled):hover, +.shadow-button:not(:disabled):active { + background-color: var(--color-background); +} + +[data-theme="dark"] .shadow-button, +[data-theme="high-contrast"] .shadow-button { + background-color: var(--color-default-subtle); + box-shadow: none; +} + +[data-theme="dark"] .shadow-button:not(:disabled):hover, +[data-theme="dark"] .shadow-button:not(:disabled):active, +[data-theme="high-contrast"] .shadow-button:not(:disabled):hover, +[data-theme="high-contrast"] .shadow-button:not(:disabled):active { + background-color: var(--color-surface-9); + box-shadow: none; +} diff --git a/src/styles/slide.css b/src/styles/slide.css index 334eea0..36560bf 100644 --- a/src/styles/slide.css +++ b/src/styles/slide.css @@ -50,7 +50,8 @@ } .slide__text-container > .eyebrow { - font-size: 0.675rem; + letter-spacing: -0.025em; + text-transform: capitalize; margin-left: 2px; margin-bottom: 0; }