├── 404 └── index.html ├── .gitignore ├── .nojekyll ├── 404.html ├── CNAME ├── README.md ├── about.md ├── about └── index.mdx ├── api ├── all.json ├── blog │ ├── 5-ways-to-not-shoot-yourself-in-the-foot-with-css │ │ └── index.json │ ├── a-conceptual-look-at-theming │ │ └── index.json │ ├── an-update-on-the-hamburger-menu │ │ └── index.json │ ├── buckets-and-jumpoffs-using-content-centric-contextual-navigation │ │ └── index.json │ ├── building-blocks │ │ └── index.json │ ├── code-literacy │ │ └── index.json │ ├── color-palette-documentation-for-living-style-guides │ │ └── index.json │ ├── components │ │ └── index.json │ ├── defining-component-apis-in-react │ │ └── index.json │ ├── design-graph │ │ └── index.json │ ├── designing-in-the-browser-faster │ │ └── index.json │ ├── development-driven-design-systems │ │ └── index.json │ ├── domain-specific-design-languages │ │ └── index.json │ ├── dont-boil-the-ocean │ │ └── index.json │ ├── hamburgers-and-basements-why-not-to-use-left-nav-flyouts │ │ └── index.json │ ├── how-react-geomicons-works │ │ └── index.json │ ├── i-dont-know-how-to-design │ │ └── index.json │ ├── im-sick-of-your-tiny-tiny-type │ │ └── index.json │ ├── interior-scene-transitions │ │ └── index.json │ ├── interoperability │ │ └── index.json │ ├── iterations-on-a-theme │ │ └── index.json │ ├── its-okay-to-look-the-same │ │ └── index.json │ ├── making-sourdough-and-learning-unity │ │ └── index.json │ ├── mathematical-web-typography │ │ └── index.json │ ├── microbeats-is-the-best-journal-ive-ever-kept │ │ └── index.json │ ├── mini-macbook-review │ │ └── index.json │ ├── modular-characters-part-one │ │ └── index.json │ ├── modular-characters-part-two │ │ └── index.json │ ├── negative-space-in-react │ │ └── index.json │ ├── organizing-mobile-navigation-based-on-information-seeking-behavior │ │ └── index.json │ ├── patterns-for-style-composition-in-react │ │ └── index.json │ ├── portability │ │ └── index.json │ ├── progressive-documentation │ │ └── index.json │ ├── rethinking-variables-in-css │ │ └── index.json │ ├── static-site-generation-with-react-and-webpack │ │ └── index.json │ ├── the-advantages-of-table-views-over-left-nav-flyouts │ │ └── index.json │ ├── the-cascade-is-not-inheritance │ │ └── index.json │ ├── the-good-the-bad-and-the-cascade │ │ └── index.json │ ├── the-modern-front-end-design-system │ │ └── index.json │ ├── the-mystery-of-the-mustard-people │ │ └── index.json │ ├── the-three-tenets-of-styled-system │ │ └── index.json │ ├── themeability │ │ └── index.json │ ├── two-steps-forward │ │ └── index.json │ ├── unit-testing-ui │ │ └── index.json │ ├── unity-inputsystem-detector │ │ └── index.json │ ├── universal-ui-components │ │ └── index.json │ ├── why-you-should-learn-css-in-js │ │ └── index.json │ └── zero-configuration-react-static-site-generator │ │ └── index.json └── index.json ├── blog ├── 5-ways-to-not-shoot-yourself-in-the-foot-with-css │ └── index.html ├── a-conceptual-look-at-theming │ └── index.html ├── an-update-on-the-hamburger-menu │ └── index.html ├── buckets-and-jumpoffs-using-content-centric-contextual-navigation │ └── index.html ├── building-blocks │ └── index.html ├── code-literacy │ └── index.html ├── color-palette-documentation-for-living-style-guides │ └── index.html ├── components │ └── index.html ├── defining-component-apis-in-react │ └── index.html ├── design-graph │ └── index.html ├── designing-in-the-browser-faster │ └── index.html ├── development-driven-design-systems │ └── index.html ├── domain-specific-design-languages │ └── index.html ├── dont-boil-the-ocean │ └── index.html ├── hamburgers-and-basements-why-not-to-use-left-nav-flyouts │ └── index.html ├── how-react-geomicons-works │ └── index.html ├── i-dont-know-how-to-design │ └── index.html ├── im-sick-of-your-tiny-tiny-type │ └── index.html ├── index.html ├── interior-scene-transitions │ └── index.html ├── interoperability │ └── index.html ├── iterations-on-a-theme │ └── index.html ├── its-okay-to-look-the-same │ └── index.html ├── making-sourdough-and-learning-unity │ └── index.html ├── mathematical-web-typography │ └── index.html ├── microbeats-is-the-best-journal-ive-ever-kept │ └── index.html ├── mini-macbook-review │ └── index.html ├── modular-characters-part-one │ └── index.html ├── modular-characters-part-two │ └── index.html ├── negative-space-in-react │ └── index.html ├── notes │ └── index.html ├── organizing-mobile-navigation-based-on-information-seeking-behavior │ └── index.html ├── patterns-for-style-composition-in-react │ └── index.html ├── portability │ └── index.html ├── progressive-documentation │ └── index.html ├── rethinking-variables-in-css │ └── index.html ├── static-site-generation-with-react-and-webpack │ └── index.html ├── the-advantages-of-table-views-over-left-nav-flyouts │ └── index.html ├── the-cascade-is-not-inheritance │ └── index.html ├── the-good-the-bad-and-the-cascade │ └── index.html ├── the-modern-front-end-design-system │ └── index.html ├── the-mystery-of-the-mustard-people │ └── index.html ├── the-three-tenets-of-styled-system │ └── index.html ├── themeability │ └── index.html ├── two-steps-forward │ └── index.html ├── unit-testing-ui │ └── index.html ├── unity-inputsystem-detector │ └── index.html ├── universal-ui-components │ └── index.html ├── why-you-should-learn-css-in-js │ └── index.html └── zero-configuration-react-static-site-generator │ └── index.html ├── build.js ├── favicon.png ├── images ├── _redirects ├── arrow-box.svg ├── autoforma.svg ├── avatar-dark.svg ├── avatar.png ├── avatar.svg ├── bot-term.svg ├── brent-jackson.jpg ├── bubo.svg ├── color.svg ├── conveyor.svg ├── design-graph-1.png ├── design-graph-1.webp ├── design-graph-2.png ├── design-graph-2.webp ├── design-graph-3.png ├── design-graph-3.webp ├── design-graph-4.png ├── design-graph-4.webp ├── double-helix.svg ├── hexagon.svg ├── isolated-interior │ ├── devlog-04.jpg │ ├── isolated-interior-inside.jpg │ └── isolated-interior-outside.jpg ├── laser.svg ├── magplate.svg ├── modular-npcs │ ├── blender-parts.jpg │ ├── early-aries.jpg │ ├── early-sketches.jpg │ ├── game-objects.jpg │ ├── in-game-crowd.jpg │ ├── in-game-crowd.webp │ ├── main-characters-before-after.jpg │ ├── mixamo-animation.jpg │ ├── mixamo-rigging.jpg │ ├── modular-characters.jpg │ ├── modular-npc-walk.mp4 │ ├── old-extras.jpg │ ├── textures.jpg │ └── unity-scripts.jpg ├── mustard │ ├── addressables.png │ ├── bank.png │ ├── bench.jpg │ ├── benches.jpg │ ├── character-texture.png │ ├── coffee.jpg │ ├── hotel.jpg │ ├── hotel.png │ ├── optimize-mesh.png │ ├── pizza.jpg │ ├── street.jpg │ ├── tacos.png │ ├── techshop.png │ └── temple.png ├── novantica-promo.jpg ├── stripe.svg ├── vantom.svg └── warning-stripes.svg ├── index.html ├── package-lock.json ├── package.json ├── principles └── index.mdx ├── src ├── 404.js ├── Blog.js ├── ColorModeButton.js ├── Devlog.js ├── Footer.js ├── Head.js ├── Header.js ├── Home.js ├── Html.js ├── Nav.js ├── Post.js ├── devlog │ ├── interior-scene-transitions.md │ ├── making-sourdough-and-learning-unity.md │ ├── modular-characters-part-one.md │ └── the-mystery-of-the-mustard-people.md └── posts │ ├── 5-ways-to-not-shoot-yourself-in-the-foot-with-css.md │ ├── a-conceptual-look-at-theming.md │ ├── an-update-on-the-hamburger-menu.md │ ├── buckets-and-jumpoffs-using-content-centric-contextual-navigation.md │ ├── building-blocks.md │ ├── code-literacy.md │ ├── color-palette-documentation-for-living-style-guides.md │ ├── components.md │ ├── defining-component-apis-in-react.md │ ├── design-graph.md │ ├── designing-in-the-browser-faster.md │ ├── development-driven-design-systems.md │ ├── domain-specific-design-languages.md │ ├── dont-boil-the-ocean.md │ ├── hamburgers-and-basements-why-not-to-use-left-nav-flyouts.md │ ├── how-react-geomicons-works.md │ ├── i-dont-know-how-to-design.md │ ├── im-sick-of-your-tiny-tiny-type.md │ ├── interoperability.md │ ├── iterations-on-a-theme.md │ ├── its-okay-to-look-the-same.md │ ├── mathematical-web-typography.md │ ├── microbeats-is-the-best-journal-ive-ever-kept.md │ ├── mini-macbook-review.md │ ├── modular-characters-part-two.md │ ├── negative-space-in-react.md │ ├── notes.md │ ├── organizing-mobile-navigation-based-on-information-seeking-behavior.md │ ├── patterns-for-style-composition-in-react.md │ ├── portability.md │ ├── progressive-documentation.md │ ├── rethinking-variables-in-css.md │ ├── static-site-generation-with-react-and-webpack.md │ ├── the-advantages-of-table-views-over-left-nav-flyouts.md │ ├── the-cascade-is-not-inheritance.md │ ├── the-good-the-bad-and-the-cascade.md │ ├── the-modern-front-end-design-system.md │ ├── the-three-tenets-of-styled-system.md │ ├── themeability.md │ ├── two-steps-forward.md │ ├── unit-testing-ui.md │ ├── unity-inputsystem-detector.md │ ├── universal-ui-components.md │ ├── why-you-should-learn-css-in-js.md │ └── zero-configuration-react-static-site-generator.md ├── static └── source-code-pro-semibold-regular.json └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | public 3 | -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/.nojekyll -------------------------------------------------------------------------------- /404.html: -------------------------------------------------------------------------------- 1 | 2 | Jxnblk | Brent Jackson
v512.16.0
Jxnblk

ERR 404
-------------------------------------------------------------------------------- /404/index.html: -------------------------------------------------------------------------------- 1 | 2 |
404
-------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | blog.jxnblk.com -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # https://jxnblk.com 3 | 4 | Personal site and blog with thoughts on design, development, and other things 5 | 6 | 7 | -------------------------------------------------------------------------------- /about.md: -------------------------------------------------------------------------------- 1 | 2 | Since 2020, I've been teaching myself game development with Unity and have been working on [Novantica][], 3 | a sci-fi adventure game, set in a futuristic urban open world where time travel is common, 4 | god-like AI govern the world, and a mysterious event has disrupted the city. 5 | You can [wishlist Novantica on Steam][novantica-steam]. 6 | 7 | Over the years, I've worked on several open source projects that helped defined modern approaches to styling front end applications. 8 | Starting in 2013, I worked with Adam Morse and John Otander on [CSS Stats][], a web application to statically analyze a site's CSS. 9 | In 2015, I created [Basscss][], the first atomic/utility-based CSS framework that helped popularize using multiple classnames 10 | on a single element to style HTML. 11 | Basscss used a small set of theme values for font-size, color palettes, margin and padding scales, and more, 12 | which is now common practice in front end design systems. 13 | If you've seen (or cursed at) code that looks like \`class="block mb2 blue"\`, then you've seen my work. 14 | In 2017, I released v1 of [Rebass][] (also my first time on [Hacker News][rebass-hn]), 15 | a library of stateless functional React components with styling, 16 | which helped introduce the concept of styled UI components in React. 17 | Extracting the utility style props from Rebass, I created [Styled System][] in 2017. 18 | In 2019, I released [Theme UI][], an extension of these ideas that provided utilities for dark mode, styling content in MDX, 19 | and a universal \`sx\` prop to style elements inline with theme-based values. 20 | 21 | I've also worked on various other open source projects. 22 | I created [MDX Deck][] as a simple way to make slide presentations with MDX. 23 | Helped John Otander with the conceptual design and syntax of [MDX][], a combination of markdown and JSX. 24 | Created several color tools, including [Colorable][] for testing color contrast, Palx, [Spectral][], and [Hello Color][]. 25 | SVG-related tools, including [Loading][], [Geomicons][], Microicon, [Paths][], and [Reline][]. 26 | 27 | As a retired DJ, I also produce electronic music, previously running a project called [Microbeats][], 28 | in which each track was created in one hour or less. 29 | I am one-half of [MrsJxn][], a collaboration with Adam Morse. 30 | I also dabble with interactive music apps like [Skullcat][]. 31 | Checkout my [SoundCloud][]. 32 | 33 | I am currently working as a design engineer at [Val Town][]. 34 | Previously, I worked at Gatsby JS, Priceline, The Grid, Etsy, Kickstarter, Stitch Fix, Living Social, and Opower. 35 | I studied sociology, Japanese language, and graphic design at Marshall University. 36 | 37 | [val town]: https://val.town 38 | [css stats]: https://cssstats.com 39 | [basscss]: https://basscss.com 40 | [rebass]: https://github.com/rebassjs/rebass 41 | [styled system]: https://github.com/styled-system/styled-system 42 | [theme ui]: https://github.com/system-ui/theme-ui 43 | 44 | [mdx deck]: https://github.com/jxnblk/mdx-deck 45 | [mdx]: https://mdxjs.com/community/about/ 46 | [colorable]: https://colorable.jxnblk.com/ 47 | [palx]: https://github.com/jxnblk/palx 48 | [spectral]: https://jxnblk.io/Spectral/ 49 | [hello color]: https://jxnblk.io/hello-color 50 | 51 | [geomicons]: https://github.com/jxnblk/geomicons-wired 52 | [loading]: https://jxnblk.io/loading/ 53 | [paths]: https://jxnblk.io/paths/ 54 | [reline]: https://jxnblk.io/reline/ 55 | 56 | [rebass-hn]: https://news.ycombinator.com/item?id=14704552 57 | 58 | [microbeats]: https://microbeats.cc/ 59 | [mrsjxn]: https://soundcloud.com/mrsjxn 60 | [soundcloud]: https://soundcloud.com/jxnblk 61 | [skullcat]: https://jxnblk.io/skullcat/ 62 | [novantica]: https://novanticagame.com 63 | [novantica-steam]: https://store.steampowered.com/app/2437530/Novantica/ 64 | 65 | -------------------------------------------------------------------------------- /about/index.mdx: -------------------------------------------------------------------------------- 1 | import { Container } from '../blocks' 2 | 3 | export default Container 4 | 5 | # About 6 | 7 | ![](/brent-jackson.jpg) 8 | 9 | Brent Jackson lives in Brooklyn, NY with his partner and calico roommate. 10 | Originally from Huntington, West Virginia, 11 | he studied Sociology, Graphic Design, and Japanese Language at Marshall University. 12 | 13 | His career in tech has led him from Web and interaction design to user experience design to front-end development. 14 | Jackson currently works at [Gatsby][], 15 | previously at Kickstarter, Etsy, LivingSocial, Stitch Fix, and Priceline. 16 | He has worked on several open source projects, including [Basscss][], [CSS Stats][], [Colorable][], [Rebass][], [Styled System][], [Theme UI][], and [MDX Deck][]. 17 | 18 | [Gatsby]: https://gatsbyjs.org 19 | [Theme UI]: https://theme-ui.com 20 | [basscss]: https://basscss.com 21 | [css stats]: https://cssstats.com 22 | [colorable]: https://colorable.jxnblk.com 23 | [rebass]: https://rebassjs.org 24 | [styled system]: https://styled-system.com 25 | [theme ui]: https://theme-ui.com 26 | [mdx deck]: https://github.com/jxnblk/mdx-deck 27 | -------------------------------------------------------------------------------- /api/blog/5-ways-to-not-shoot-yourself-in-the-foot-with-css/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/5-ways-to-not-shoot-yourself-in-the-foot-with-css","slug":"5-ways-to-not-shoot-yourself-in-the-foot-with-css","title":"5 ways to not shoot yourself in the foot with CSS","description":null,"date":"2014-12-30T00:00:00.000Z","excerpt":"
    \n
  1. Don’t make assumptions
  2. \n
  3. Don’t be specific
  4. \n
  5. Don’t override anything
  6. \n
  7. Don’t entangle it with markup
  8. \n
  9. Don’t repeat yourself
  10. \n
\n","html":"
    \n
  1. Don’t make assumptions
  2. \n
  3. Don’t be specific
  4. \n
  5. Don’t override anything
  6. \n
  7. Don’t entangle it with markup
  8. \n
  9. Don’t repeat yourself
  10. \n
\n"} -------------------------------------------------------------------------------- /api/blog/an-update-on-the-hamburger-menu/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/an-update-on-the-hamburger-menu","slug":"an-update-on-the-hamburger-menu","title":"An Update on the Hamburger Menu","description":null,"date":"2014-04-12T00:00:00.000Z","excerpt":"

About a year ago, I wrote Hamburgers & Basements: Why Not to Use Left Nav Flyouts.

\n

Since then, a few things have happened.

\n","html":"

About a year ago, I wrote Hamburgers & Basements: Why Not to Use Left Nav Flyouts.

\n

Since then, a few things have happened.

\n\n
    \n
  1. Facebook has discontinued using the hamburger menu in their iOS app.
  2. \n
  3. A few desktop websites have replaced their persistent navigation with the hamburger menu – who knows if they're actually testing it or how they're interpreting the results.
  4. \n
  5. Time Magazine decided to use it:
  6. \n
\n
\n

\"No one understands the icon, let's add the word menu. The word is too small, let's add a pop-up calling it out.\" pic.twitter.com/Jargi7gavX

\n

— Luke Wroblewski (@lukew) March 11, 2014

\n
\n
    \n
  1. Personally, I think user testing the hamburger menu is good enough, but these guys A/B tested it: UX designers: Side drawer navigation could be costing you half your user engagement
  2. \n
  3. This guy tested it twice: Mobile Menu AB Tested: Hamburger Not the Best Choice? and Hamburger vs Menu: The Final AB Test
  4. \n
  5. This guy also wrote about it: The Hamburger is Bad for You
  6. \n
\n

Despite all of this, I still haven't seen any evidence suggesting that the hamburger menu is an improvement.

\n

Patterns

\n

Basically it comes down to interface design patterns. Patterns rely on familiarity and emerge slowly over time. Most of the ones we use on the web today have been around for many years.

\n

Users have plenty of new things to learn without adding contrived navigation patterns into the mix. Let's stop trying to innovate device-specific interactions and leave it to the device manufacturers.

\n

Let's focus on the real problems

\n

Product design requires solving many more difficult problems.\nFor example:

\n\n

Personally, I'd much rather be designing and testing solutions for problems like these.

\n"} -------------------------------------------------------------------------------- /api/blog/buckets-and-jumpoffs-using-content-centric-contextual-navigation/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/buckets-and-jumpoffs-using-content-centric-contextual-navigation","slug":"buckets-and-jumpoffs-using-content-centric-contextual-navigation","title":"Buckets and Jumpoffs: Using Content-Centric Contextual Navigation","description":null,"date":"2012-12-07T00:00:00.000Z","excerpt":"

While table views provide a clear and simple way to navigate certain types of content, mobile should be about putting content and user goals first and navigation second. Don't overload the user with navigation choices, show meaningful content instead. Even though tab bars are great – sitting below the content, out of the way until the user needs them – there are new opportunities to explore content-centric contextual navigation when designing for mobile.

\n","html":"

While table views provide a clear and simple way to navigate certain types of content, mobile should be about putting content and user goals first and navigation second. Don't overload the user with navigation choices, show meaningful content instead. Even though tab bars are great – sitting below the content, out of the way until the user needs them – there are new opportunities to explore content-centric contextual navigation when designing for mobile.

\n\n

Create Better Table Views with Teasers

\n

\"Mail

\n

A quick and dirty way to give table views more meaning is by using teasers. Apple's Mail App shows a numerical indicator of  unread emails at the top level, then shows truncated previews of actual messages at the inbox level. Similarly, Messages shows previews of the last message sent before the user drills into the conversation.

\n

Flipping the Teaser Approach on Its Head

\n

While teasers can make basic table views more meaningful, they prioritize navigation over content. Taking the teaser approach and flipping it on its head can result in a content-centric design that provides meaningful contextual navigation to the user. Many apps and e-commerce sites already do this, and it can help create focus on content discovery rather than search.

\n

What are Buckets & Jumpoffs?

\n

You've probably seen this pattern before: take a collection of items, group them into categories, present a limited number of those items in buckets or lists, and provide a link to jump off and view more. There doesn't seem to be a great name for this pattern, some have suggested calling it the egg carton or the showcase, but neither of those terms seem to capture the essence of the jumpoff link's contextual navigation – thus, buckets & jumpoffs.

\n

\"Twitter\"

\n

Twitter uses this approach in its profile view, along with a mix of standard table views. Instead of seeing all of the user's tweets in the profile view, there is a small bucket showing the latest three tweets and a link to view more.

\n

\"Rdio\"

\n

Rdio does this to great effect in its search results view, showing results bucketed by artists, albums, songs, playlists, and people. This view provides direct links to the top results and a link to view more results in each bucket as well.

\n

\"App

\n

Using a mixture of swipeable carousel buckets and tiles, Apple creates a lot of visual interest in its App Store and iTunes apps. These Featured views are frequently updated and serve as a great way to promote content discovery in an otherwise extremely complex IA. Apple also uses buckets on its desktop version of iTunes, showing that this pattern can be effectively scaled up to larger displays.

\n

\"Foursquare\"

\n

Similarly, Foursquare places contextual navigation – which is often delegated to detail views – in-line with its content-rich list view. Unfortunately, Foursquare's desktop site lacks the same level of content discovery afforded in its mobile app and, instead, relies heavily on traditional search.

\n

Focus on Content but Don't Neglect Navigation

\n

Mobile is a new medium, and its constraints and advantages should be taken into account when designing navigation systems. The limited screen real estate makes traditional faceted filtering and search patterns more difficult. Instead of replicating desktop navigation-heavy approaches, try content-centric contextual navigation. To create a cohesive cross-platform experience, you can translate mobile navigation patterns to desktop. This often leads to simple, focused interfaces that will delight your users.

\n

When designing for mobile, focus on content, but don't neglect navigation in the process.

\n"} -------------------------------------------------------------------------------- /api/blog/building-blocks/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/building-blocks","slug":"building-blocks","title":"Building Blocks","description":null,"date":"2019-03-17T00:00:00.000Z","excerpt":"

People like building blocks.

\n

IKEA effect

\n\n","html":"

People like building blocks.

\n

IKEA effect

\n\n","draft":true} -------------------------------------------------------------------------------- /api/blog/code-literacy/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/code-literacy","slug":"code-literacy","title":"Code Literacy","description":"Asking should designers code is asking the wrong question.","date":"2019-07-25T00:00:00.000Z","excerpt":"Asking should designers code is asking the wrong question.","html":"

Ah yes, everyone's favorite question: should designers code?\nAs much as I'd love to pontificate on the matter,\nI think the framing of this question is fundamentally wrong.\nAt the heart of this conversation is the idea of code literacy,\nthat is, the level at which one can read, write, and understand software code in the form of programming languages.

\n

We don't question whether or not we should teach children to read and write,\nor whether we should math in school.\nIn a modern public education systems, these are a given.\nWhile these institutions might have other problems,\nI think code literacy should be a fundamental part of any school curriculum.

\n

What is code? Paul Ford has already written more than enough to cover that topic,\nbut I'd like to step back and consider how code is not dissimilar to written language and mathematics.

\n

While spoken languages are innate, we humans invented written language somewhat recently in our own history.\nSpeaking comes naturally, but written language is not hard-wired into our brains.\nWe naturally think in symbols, and most of us are able to handle reading and writing,\nbut it's still something that we invented.\nIt's a powerful tool that allows us to transfer knowledge over space and time in ways we couldn't do without it.

\n

Math, too, is symbolic in nature.\nIt's not entirely dependent on written language,\nbut math certainly benefits from our ability to scribble lines on paper.

\n

Code is the languages we humans use to communicate with computers.

\n

Think about this for a second:

\n\n

The computer is the first tool that we've created that requires written language to even work.

\n

Without software, computers are useless.\nPerhaps, one day, computers might fluently speak natural human languages,\nbut there will always be code underlying the abstractions that enable this.

\n

Some people are interested in abstracting away the code required to program computers,\nwhich can make computers accessible in ways they weren't before.\nBut saying that code will become less and less fundamental to the progress of technology\nis akin to saying that no one needs to learn mathematics because we have calculators now.\nIf anything, the opposite is true.

\n

I suspect that the invention of software might be on par with the invention of mathematics,\nbut we're too close to the event to see its historical significance.\nMathematics is broad and broadly applicable and works at a much lower level than code -\nsoftware isn't really possible without mathematics, afterall.\nBut this is the first time in human history where we've created a technology that requires written language (i.e. software) to operate.

\n

Not everyone in a modern society needs to be a poet, or a bestselling author, or have a PhD in mathematics,\nbut I think having basic reading, writing, and arithmetic skills will benefit anyone.\nI think the same is true for code literacy.\nNot everyone needs to be a software developer,\nbut the more you know about how humans communicate with machines,\nthe more benefits you'll see professionally.

\n

Imagine you work in the finance industry and have figured out a way to mitigate all the billions of dollars lost on spreadsheet errors.\nOr imagine you're a farmer who leverages machine learning to predict crop yields.\nYou could wait for software developers to create this for you,\nbut diverse experiences applied to different technologies can result in incredible innovation.

\n

I don't think we should expect someone in a non-developer role to read and write code professionally.\nBut I also think the people who are curious and who learn a little more than their peers\nwill always have the advantage.

\n"} -------------------------------------------------------------------------------- /api/blog/components/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/components","slug":"components","title":"Components","description":null,"date":"2016-06-17T00:00:00.000Z","excerpt":"

Everything in a UI is a component.\nThis includes buttons, inputs, forms, promotional modules, pages, user flows, etc.\nI use the word component not only because this is how the underlying code is written in libraries like React and Ember,\nbut also because pieces of a well-designed UI system should be composable.

\n","html":"

Everything in a UI is a component.\nThis includes buttons, inputs, forms, promotional modules, pages, user flows, etc.\nI use the word component not only because this is how the underlying code is written in libraries like React and Ember,\nbut also because pieces of a well-designed UI system should be composable.

\n\n

From the Wikipedia page on Composability:

\n
\n

A highly composable system provides recombinant components that can be selected and assembled in various combinations to satisfy specific user requirements. In information systems, the essential features that make a component composable are that it be:

\n\n
\n

Modular & Stateless Components

\n
state => ui\n
\n

A UI system that is made up of independent stateless components is extremely flexible.\nWhen individual pieces need to be swapped out or updated,\nthose changes are isolated and don’t cause other parts of a system to break.\nThinking about these components as being pure functions –\nthat is, the same state always produces the same output –\ncan help ensure composability.

\n
\n

A pure function is one that exhibits the property of substitution: replacing a call with its returned value should make the program equivalent. As an example, concat('hello', 'world') can be substituted with 'hello world' without changing the behavior of your program.

\n

How can we apply this to a graphical user interface? By having the function return an abstract representation of widgets (or markup) to be rendered on the screen...

\n

Pure UI by Guillermo Rauch

\n
\n

Why is everything a “component?”

\n

Naming things is hard, there’s no debate there, but when you start to categorize different parts of a UI into pages, views, flows, atoms, molecules, materials, or kittens, you’ve already started to undermine the concept of composability, and it probably takes more time and effort to get an entire team of people to “agree upon” your proposed naming conventions than it’s worth.

\n

The point of this is to think about everything as an interoperable system.\nYou can slice and dice components in any way you see fit, and these components are likely to change and be fine tuned as a system is developed.\nPremature optimization is a trap that’s easy to fall into.\nEmbrace the chaos as you build.\nPatterns will emerge from the primordial goop of UI that is your product,\nand by consistently thinking about a composable system you’ll probably come up with something more flexible\nand more robust than if one person dictates a dogmatic framework to work within.

\n"} -------------------------------------------------------------------------------- /api/blog/designing-in-the-browser-faster/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/designing-in-the-browser-faster","slug":"designing-in-the-browser-faster","title":"Designing in the Browser Faster","description":null,"date":"2014-04-14T00:00:00.000Z","excerpt":"

I’ve been dabbling with HTML and CSS for years—building small websites for myself and friends and building prototypes to test designs. And, while I’ve been fascinated with the idea of designing in the browser for a long time, it wasn’t until recently that it’s become much, much faster for me than using traditional design software.

\n","html":"

I’ve been dabbling with HTML and CSS for years—building small websites for myself and friends and building prototypes to test designs. And, while I’ve been fascinated with the idea of designing in the browser for a long time, it wasn’t until recently that it’s become much, much faster for me than using traditional design software.

\n\n

Getting faster

\n

Practice has certainly helped, but what really sped up my ability to design and iterate in code was an approach called Object Oriented CSS, or OOCSS. My friend and colleague sent me down this scary-sounding path over a year ago, and I haven’t looked back since. He gave me a ton of reading material, coached me with code reviews, and constantly challenged me. Most of the articles on OOCSS focus on front-end performance, code maintenance benefits, and things like naming conventions. What most people don’t tell you is how much faster it can make designing in code.

\n

Do one thing well

\n

At its core, OOCSS focuses on highly reusable styles that follow the open/closed principle—that is, they’re open for extension, but closed for modification. They do one thing and do it well. You can think of them as something like layer effects or color swatches. When I’m fleshing out a design, I spend a lot of time adjusting spacing, font sizes, colors, and other small details. I often don’t know what combination of styles each element should have before I see it in context.

\n

Don’t make assumptions

\n

For new web projects, I usually start with a base type scale, spacing scale, and a rough color palette. Having these separated into single-purpose utilities gives me the flexibility to jump straight into HTML and experiment, without having to constantly write and rewrite CSS. I try not to make assumptions about what any one element or module will end up looking like. Instead, I make sure each element feels right in context, then create more defined patterns when needed.

\n

Content-centric design

\n

Using single-purpose styles aligns well with the concept of content-centric design. Defining heading styles without knowing what they say or where they’re located can be difficult. Having the ability to quickly adjust type hierarchy, color, and other styles to get the rhythm, balance, and gestalt right is key to successfully designing in the browser.

\n

From paper to prototype

\n

Using this approach, I typically go directly from notes and paper sketches to building prototypes in code. The only time I open graphic applications like Illustrator or Photoshop are when I need to create image assets to use in code. And with tools like Github, I can try out a number of iterations on different branches in a non-destructive way.

\n

Choose the right tools

\n

Personally, this has sped up my workflow tremendously, and every designer I’ve introduced to this approach has picked it up quickly and enthusiastically. You might find OOCSS to be a useful addition to your design toolkit as well.

\n"} -------------------------------------------------------------------------------- /api/blog/domain-specific-design-languages/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/domain-specific-design-languages","slug":"domain-specific-design-languages","title":"Domain-Specific Design Languages","description":null,"date":"2016-06-30T00:00:00.000Z","excerpt":"\n

Deviating from the HIG means you are creating a new design language specific to your application,\nwhich may have its benefits, but also requires more learning on the part of the user.

\n

Similarly, deviating from a web application's style guide, UI component library, or interaction pattern library\nis equivalent to creating a new design language that users need to learn.

\n

The web itself has it's own design language.\nUsers have acclimated to using blue underlined links, scrolling through pages, using standard form elements,\nas well as a plethora of other interaction design patterns.

\n

Though sometimes DSLs are appropriate,\nthe main disadvantage to using a DSL is that it requires domain-specifig knowledge of certain APIs rather than relying on more standard conventions. An example of this would be using coffeescript or JSX, rather than the standardized JavaScript (EMCAScript) syntax

\n","html":"\n

Deviating from the HIG means you are creating a new design language specific to your application,\nwhich may have its benefits, but also requires more learning on the part of the user.

\n

Similarly, deviating from a web application's style guide, UI component library, or interaction pattern library\nis equivalent to creating a new design language that users need to learn.

\n

The web itself has it's own design language.\nUsers have acclimated to using blue underlined links, scrolling through pages, using standard form elements,\nas well as a plethora of other interaction design patterns.

\n

Though sometimes DSLs are appropriate,\nthe main disadvantage to using a DSL is that it requires domain-specifig knowledge of certain APIs rather than relying on more standard conventions. An example of this would be using coffeescript or JSX, rather than the standardized JavaScript (EMCAScript) syntax

\n","draft":true} -------------------------------------------------------------------------------- /api/blog/dont-boil-the-ocean/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/dont-boil-the-ocean","slug":"dont-boil-the-ocean","title":"Don't Boil the Ocean","description":null,"date":"2019-03-18T00:00:00.000Z","excerpt":"\n","html":"\n","draft":true} -------------------------------------------------------------------------------- /api/blog/how-react-geomicons-works/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/how-react-geomicons-works","slug":"how-react-geomicons-works","title":"How react-geomicons Works","description":null,"date":"2015-06-10T00:00:00.000Z","excerpt":"

Geomicons Open is an SVG icon set completely hand-coded using the Path element.\nThe first version’s source was built in complete SVG, but I soon realized that the wrapping SVG and Path elements were the exact same for each icon.\nIn an effort to DRY things up, I set up a rudimentary build process that would take source code containing only the Path elements’ d attribute value and create valid SVG code.

\n

I also built a rough icon injection script...

\n\n","html":"

Geomicons Open is an SVG icon set completely hand-coded using the Path element.\nThe first version’s source was built in complete SVG, but I soon realized that the wrapping SVG and Path elements were the exact same for each icon.\nIn an effort to DRY things up, I set up a rudimentary build process that would take source code containing only the Path elements’ d attribute value and create valid SVG code.

\n

I also built a rough icon injection script...

\n\n","draft":true,"tags":["react","svg","icons","geomicons"]} -------------------------------------------------------------------------------- /api/blog/i-dont-know-how-to-design/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/i-dont-know-how-to-design","slug":"i-dont-know-how-to-design","title":"I don’t know how to design","description":null,"date":"2014-04-16T00:00:00.000Z","excerpt":"

I’ll admit it.

\n","html":"

I’ll admit it.

\n\n

I don’t know how to design without research.

\n

Without upfront research, you’re not designing things, you’re just building things that might or might not meet your users’ needs or fit their mental models. Chances are, they won’t. User acceptance testing and A/B testing are important, but won’t give you the insights you’ll need to start off on the right foot.

\n

I don’t know how to design without metrics.

\n

Whether it’s business goals, KPIs, or conversion rates, without metrics you’ll have no idea what problems you’re solving or if the solutions you’re building will fit the bill.

\n

I don’t know how to design without iterating.

\n

Your first attempt at anything will not be your best. Put something out there and continually work on making it better and better.

\n

I don’t know how to design without context.

\n

Good design simplifies complexity to align with users’ mental models. Without understanding context and creating systems to design within, you’re just adding noise that contributes to cognitive overload for both users and the people building the product.

\n

I don’t know how to design without collaboration.

\n

Design doesn’t happen in a vacuum. Whether it’s through participatory design with users, sketching sessions with other designers, developers, product managers, and stakeholders, or just an old fashioned critique, the best design work comes from working with others.

\n"} -------------------------------------------------------------------------------- /api/blog/im-sick-of-your-tiny-tiny-type/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/im-sick-of-your-tiny-tiny-type","slug":"im-sick-of-your-tiny-tiny-type","title":"I’m Sick of Your Tiny, Tiny Type","description":null,"date":"2013-01-29T00:00:00.000Z","excerpt":"

Your tiny type is hard to read – no, not hard to read, impossible to read. I carry my phone with me everywhere, but I always seem to forget my magnifying glass. I tap the Safari Reader button, but that’s not a solution to the problem. That’s a band-aid for your bad typesetting.

\n","html":"

Your tiny type is hard to read – no, not hard to read, impossible to read. I carry my phone with me everywhere, but I always seem to forget my magnifying glass. I tap the Safari Reader button, but that’s not a solution to the problem. That’s a band-aid for your bad typesetting.

\n\n

Sometimes I’m on my computer, and Reader doesn’t work on your web app. I hit CMD + two or three times so that my dyslexic brain can make sense of the musty 14px Helvetica your servers regurgitated all over my screen. Then the layout falls apart. Words start smashing together. Ads bleed into my emails. And I find myself scrolling up, down, left, right, left, right – what in the hell is this? The Konami code? I don’t need 30 extra lives. I want to read your content.

\n

And I know I’m not the only one who hates your tiny type. How many times have I heard users complain about fonts being too small? More times than I’ve heard them complain about fonts being too large – wait, I’ve never heard a user complain about that. Your users aren’t asking for a faster horse – they’re struggling to read your content. Surely that isn’t what you’re going for, and surely that isn’t a good experience.

\n

Further reading:

\n

Relative Readability

\n

The 100% Easy-2-Read Standard

\n

The Typography and Layout behind the new Signal vs. Noise redesign

\n"} -------------------------------------------------------------------------------- /api/blog/its-okay-to-look-the-same/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/its-okay-to-look-the-same","slug":"its-okay-to-look-the-same","title":"It’s Okay to Look the Same","description":null,"date":"2014-04-25T00:00:00.000Z","excerpt":"

Every once in a while I hear someone complain about the visual homogenization of the web, and front-end frameworks often get the brunt of the attack. This visual sameness isn’t necessarily a bad thing.

\n","html":"

Every once in a while I hear someone complain about the visual homogenization of the web, and front-end frameworks often get the brunt of the attack. This visual sameness isn’t necessarily a bad thing.

\n\n

Visual design is easy

\n

Visual design is one of the easiest parts of web design to get right. Beautiful looking sites are often perceived as being easier to use, but that’s a low hurdle to clear. Using clean typography, a good color palette, solid visual hierarchy, and consistency will get you most of the way there.

\n

It’s also hard

\n

The hardest part of visual design is stakeholder buy-in. It’s easily the most noticeable part of a design, and it’s one that people will certainly have opinions about — whether or not they understand typography, gestalt, or color psychology. If you’re the one calling the shots, then you’ve got it easy.

\n
\n

Visual design is a bike shed. User experience is a nuclear reactor.

\n
\n

It’s called Bootstrap for a reason

\n

When building something new, frameworks can help settle arguments over visual details. And, if you don’t have a background in design, using a framework can help you achieve a certain baseline of visual polish. Frameworks get you started and create well-defined systems to customize and build upon while you validate your ideas.

\n

Don’t not do it

\n

The visual aspects of web design are extremely important. If you’ve got the time and resources, by all means, sweat the details. But before you do, make sure your site works. Time spent deliberating visual design could be better spent conducting research with your users. A beautiful site that doesn’t align with mental models isn’t worth much to your business.

\n"} -------------------------------------------------------------------------------- /api/blog/microbeats-is-the-best-journal-ive-ever-kept/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/microbeats-is-the-best-journal-ive-ever-kept","slug":"microbeats-is-the-best-journal-ive-ever-kept","title":"Microbeats is the Best Journal I’ve Ever Kept","description":null,"date":"2012-11-07T00:00:00.000Z","excerpt":"

I’ve been producing electronic music on my computer for about a decade now, and I don’t have a whole lot to show for it. After moving to DC from Shanghai, where I played a lot of live sets and DJ gigs, I realized there wasn’t much of a music scene in DC, and I stopped playing out. After a few years, I noticed that I generally wasn’t being inspired, and I wasn’t growing much as an artist. I also noticed that I had a tendency to never finish the tracks that I’d started. I was pretty good at creating catchy little loops, but they never evolved into anything beyond that.

\n","html":"

I’ve been producing electronic music on my computer for about a decade now, and I don’t have a whole lot to show for it. After moving to DC from Shanghai, where I played a lot of live sets and DJ gigs, I realized there wasn’t much of a music scene in DC, and I stopped playing out. After a few years, I noticed that I generally wasn’t being inspired, and I wasn’t growing much as an artist. I also noticed that I had a tendency to never finish the tracks that I’d started. I was pretty good at creating catchy little loops, but they never evolved into anything beyond that.

\n\n

To try to pull myself out of this rut, I started a Tumblr called Loop A Day in early 2011. The idea was that I would spend no more than an hour each day creating a loop and posting it to this blog. I kept up with it for about a month, then I realized that the daily routine was wrecking my social life – I remember leaving parties more than once just to go home and work on music. I did like the hour limitation, but doing it every day had to stop. So, I got rid of my self-imposed quota and renamed the project Microbeats.

\n

A lot of the earlier beats I created were technical experimentations, and I drew a lot of inspiration from the music I was listening to. Then I started noticing something happening. I started subconsciously pulling inspiration from my actual life and putting more emotion into the beats I was creating. By the end of 2011, I started consciously pulling inspiration from the things I was doing, the places I was going, the people I was meeting and the conversations I was having and using that as mental fodder when I sat down to work on a beat.

\n

It may not be obvious to the outside observer, but for me, Microbeats became a record of all the things happening in my life – though some beats are admittedly (and intentionally) vulnerable. Conversations from friends’ parties, meeting new people, getting dumped, rebounding, breaking my hand, traveling for work, vacationing with family, moving to a new city, and everything in between – my life started to manifest itself in the music.

\n

Though I don’t do it very often, when I go back through the archives and listen to the beats, I start remembering things that I don’t think I would have otherwise – strange, nuanced emotions and small moments that my brain didn’t deem important enough to take good record of. Microbeats started off as a simple little experiment, but it’s grown into something so much bigger, personally, than I ever would’ve imagined.

\n

microbeats.cc

\n"} -------------------------------------------------------------------------------- /api/blog/mini-macbook-review/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/mini-macbook-review","slug":"mini-macbook-review","title":"Mini MacBook Review","description":"Why the new MacBook might just be my favorite computer ever","date":"2015-06-17T00:00:00.000Z","excerpt":"Why the new MacBook might just be my favorite computer ever","html":"

Size

\n

Fits in my pocket.

\n

Screen

\n

Super sharp.

\n

Keyboard

\n

Sounds great. pok pok pok pok pok

\n

Trackpad

\n

Clicks and clicks again, but doesn’t really click.

\n

Ports

\n

Power: check. Beats: check.

\n

Performance

\n

Runs Vim like a champ.

\n","tags":["apple","macbook","review","laptop","tech"]} -------------------------------------------------------------------------------- /api/blog/modular-characters-part-two/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/modular-characters-part-two","slug":"modular-characters-part-two","title":"Modular Characters Part II","description":null,"date":"2023-10-17T00:00:00.000Z","excerpt":"

After redesigning the NPC characters in Novantica to use modular parts and color schemes, I realized that the approach I had set up wouldn't for the game due to poor performance.

\n","html":"

After redesigning the NPC characters in Novantica to use modular parts and color schemes, I realized that the approach I had set up wouldn't for the game due to poor performance.

\n\n

This is a two-part series. Read Modular Characters Part I here.

\n\n","draft":true,"tags":["devlog","unity","blender","character design","mixamo"]} -------------------------------------------------------------------------------- /api/blog/organizing-mobile-navigation-based-on-information-seeking-behavior/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/organizing-mobile-navigation-based-on-information-seeking-behavior","slug":"organizing-mobile-navigation-based-on-information-seeking-behavior","title":"Organizing Mobile Navigation Based on Information-Seeking Behavior","description":null,"date":"2013-01-16T00:00:00.000Z","excerpt":"

When opening an application, a user should be able to understand its functionality, see relevant content, and get to where they want to go. Applications that obscure navigation with the intent of focusing on content can make finding specific information difficult. On the other hand, skewing towards too much navigation can overwhelm the user. Mobile apps should balance navigation for users with different information needs.

\n","html":"

When opening an application, a user should be able to understand its functionality, see relevant content, and get to where they want to go. Applications that obscure navigation with the intent of focusing on content can make finding specific information difficult. On the other hand, skewing towards too much navigation can overwhelm the user. Mobile apps should balance navigation for users with different information needs.

\n\n

An application's IA should be based on solid user research and mental models, but navigation should take the user context and design constraints of the platform into consideration. With desktop apps, the ample screen real estate allows for high information density and complex top-level navigation. Mobile apps require different approaches to map navigation to the product's IA. One approach is to organize navigation based on three common information seeking behaviors: known-item seeking, exploratory seeking, and discovery.

\n

What is Known-item Seeking?

\n

Known-item seeking is exactly what it sounds like: a user knows what they're looking for and seeks it out. For example, a person going to the market to pick up eggs is engaged in known-item seeking. Search can be a great way to accommodate this behavior. But with mobile apps, avoiding touchscreen keyboards is generally good practice since there's more room for error. Recognition is easier than recollection, and scanning a list of well-labeled categories can be a much quicker way to find information. Just be sure the labels make sense to users and are backed by research, using methods such as card sorting and tree testing.

\n

Exploratory Seeking

\n

Exploratory seeking is when the user has a vague idea of what they're looking for but may not know the proper words to describe it. An example of this behavior is when a person goes to the market looking for ingredients for an omelet, but isn't sure which vegetables are in season. Lists of well-labeled categories can accommodate this behavior as well.

\n

Discovery

\n

Discovery is when the user isn't looking for anything in particular – for instance, when a hungry person goes to the market with no idea what they'd like to eat. Discovery in mobile apps is often supported by displaying relevant content – as opposed to navigation – and is usually organized by time or social cues, such as popularity.

\n

Examples

\n

\"Fab.com

\n

While the navigation in Fab.com's mobile app is poorly organized and not very obvious, the app still supports known-item and exploratory seeking. Its Search view has both a text field for traditional search, and a table view navigation of categories, colors, and prices. 

\n

\"Apple

\n

The Apple Store app's navigation includes a Featured tab, which encourages discovery, and a Products tab for seeking. A table view of well-labeled product categories provides quick known-item seeking, and both tabs include a search field at the top.

\n

\"Etsy

\n

Similarly, the Etsy app supports discovery with its Explore tab and facilitates known-item and exploratory seeking with the categories listed in the Search tab. Like the desktop website, the mobile app's navigation provides obvious paths to specific items within their large inventory, but is significantly simpler at the top-level.

\n

Rethinking Mobile Navigation

\n

Designing mobile navigation requires different approaches than desktop apps. Don't try to cram a desktop-based navigation into a mobile app, and don't neglect navigation in the pursuit of making an app content-centric. Take a step back, look at your product's IA, and consider other organizing principles for designing mobile navigation.

\n"} -------------------------------------------------------------------------------- /api/blog/portability/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/portability","slug":"portability","title":"Portability","description":"Defining a portable file format for high level style information.","date":"2019-07-23T00:00:00.000Z","excerpt":"Defining a portable file format for high level style information.","html":"

In software development, formats help ensure that content and data are portable and can be used in many different applications.\nBy adhering to HTML standards set by the WHATWG and W3C, browsers created by different organizations\ncan render HTML documents created by many different people and generated in many different ways.\nThat is, HTML is a portable format that can be rendered by many different clients.\nWhile HTML is certainly one of the most successful examples of standardization ever,\nless widely used formats can still benefit from the same idea.

\n

Take Markdown as an example.\nIt was created in 2004 by John Gruber and Aaron Swartz, and today is the de facto format for writing software documentation.\nIt isn't widely used outside the field of software development,\nbut there are many different engines that can render this format into HTML.\nMarkdown has become a somewhat portable format.\nBy writing documentation, blog posts, or wiki pages in this format,\nyou can use many different tools to render the content in different places,\nand you can be fairly certain that there will always be tools that can render this format in the future.

\n

Markdown was intended to be easy-to-read and easy-to-write, and can be viewed as a simple abstraction on top of HTML.\nA complete HTML page cannot be replaced by Markdown, but all Markdown files can be written as HTML.\nMost people find markdown simpler to read and write than raw HTML, and prefer using it for things like blog posts and documentation.

\n

In the same way that Markdown has found a niche in certain contexts for authoring HTML,\nI'm very interested in taking this idea and applying it to CSS.\n\"But, CSS is already easy to read and write,\" you might say.\nSure, but when I'm considering which styles differ the most across different interfaces and which styles make the biggest impact,\nI'm mostly concerned with three things: color, typography, and layout.\nAuthoring an entire stylesheet to apply styles like this can require some effort,\nand the end result isn't as portable as you might think.\nSimply copying a stylesheet from one website to another does not guarantee that the styles will be applied the way you intend them to be.\nA lightweight abstraction for this sort of thing, however, could make these sorts of styles more portable.

\n

Unlike Markdown, I do not think this requires inventing new syntax.\nWith the existing syntaxes for CSS, JavaScript, JSON, YAML, and others, there are already plenty of tools that can handle parsing.\nThis does, however, require a specific shape or schema to be successful,\nand the Theme Specification is meant to be a simpler way to write styles for an application.\nWhile Theme UI (which is built with the Theme Specification) can output stylesheets in the form of Tachyons and other similar CSS libraries, it cannot fully replace CSS on its own.\nIt does provide a level of portability for sets of colors, typographic styles,\nand other design constraints that can be applied in many different places.\nAs the number of libraries that follow this Theme Specification increases,\nyou will be able to reuse these styles in more and more places,\nmaking it a more portable format for sharing styles.

\n"} -------------------------------------------------------------------------------- /api/blog/progressive-documentation/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/progressive-documentation","slug":"progressive-documentation","title":"Progressive Documentation","description":"Attempting to make writing documentation easier and faster","date":"2018-09-01T00:00:00.000Z","excerpt":"Attempting to make writing documentation easier and faster","html":"

When building out JS libraries and components, everyone knows the importance of good documentation.\nWithout it, good code can end up going unused, which leads to duplication.

\n

For many libraries, markdown is an excellent format for writing docs since it's based on HTML and renders nicely in a variety of tools like GitHub.com.\nBut markdown can fall a little short when documenting front-end UI components,\nwhere demos and the ability to interact with a component in the browser is immensely helpful.

\n

MDX

\n

MDX was built with some of this in mind.\nIt gives you the simplicity of writing markdown combined with the ability to import and use React components with JSX.\nMore and more tools are adding support for MDX,\nmaking using it as a documentation format a no-brainer.

\n

Documentation Tools

\n

While there are several great tools out there for creating high-quality documentation sites,\nsuch as Next.js, Gatsby, Docusaurus, and Docz,\nmany of these tools require custom setup and configuration outside of your source code\nand can be a distraction if you're trying to quickly create documentation for something new.

\n

Additionally, these tools don't always make it easy to set up documentation to serve as a development tool while working on the source code.\nThere are other tools out there for quickly developing components in isolation,\nbut they tend to use proprietary APIs and don't scale well, still requiring separate documentation as a project grows.

\n

Introducing mdx-go

\n

\n

mdx-go is a development tool for progressive documentation\nand is meant to be used alongside your tools of choice for building documentation sites.\nIt allows you to quickly prototype, draft docs, or build simple demos outside of a larger application.

\n

mdx-go is built with the following goals in mind:

\n\n

Don't boil the ocean

\n

By starting docs for a new project with mdx-go,\nyou can focus on writing docs immediately,\nand you won't be locked into custom APIs or build setups,\nmeaning that upgrading to other documentation solutions later on is easy when you're ready to.\nThe related mdx-docs project is one way to migrate a directory of MDX files over to using Next.js.

\n

mdx-go also works well as a local sandbox development environment, and you can continue to use it alongside other documentation tools.\nIt offers static site exporting for sharing work-in-progress demos or drafts with others.

\n

Choose the right tool for the job

\n

Tools like mdx-go make it easy to try things out without a lot of setup or getting locked into proprietary APIs.\nEvery team is different and has different needs, but I hope mdx-go is a helpful addition to your development toolkit.

\n

To learn more see the mdx-go docs.

\n
\n

Further Reading

\n\n"} -------------------------------------------------------------------------------- /api/blog/rethinking-variables-in-css/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/rethinking-variables-in-css","slug":"rethinking-variables-in-css","title":"Rethinking Variables in CSS","description":null,"date":"2015-05-08T00:00:00.000Z","excerpt":"

CSS was first introduced as a way to reduce the complexity of using inline styles and to help separate concerns. After years of ballooning stylesheets with the same values being used over and over and losing sync, CSS preprocessors introduced variables to help keep values defined in a single place. Soon custom properties will be part of the CSS specification, which promises a native, more robust approach than what preprocessors can do.

\n

While variables and custom properties make updating multiple instances of the same value trivial, we often still end up with multiple instances of the same property-value definitions spread throughout a global stylesheet.

\n","html":"

CSS was first introduced as a way to reduce the complexity of using inline styles and to help separate concerns. After years of ballooning stylesheets with the same values being used over and over and losing sync, CSS preprocessors introduced variables to help keep values defined in a single place. Soon custom properties will be part of the CSS specification, which promises a native, more robust approach than what preprocessors can do.

\n

While variables and custom properties make updating multiple instances of the same value trivial, we often still end up with multiple instances of the same property-value definitions spread throughout a global stylesheet.

\n\n

Bear with me for a second and consider this a thought experiment…

\n

What if instead of repeating these definitions in our stylesheets, we treated CSS rulesets as variables? That is, instead of defining something like a color across many styles, it’s only defined once, and is used by applying classes to HTML elements —i.e. .green instead of $green. We could vastly DRY up our stylesheets while making only a minimal impact on HTML size. If you apply this idea widely enough, the entire stylesheet can become so-called critical CSS.

\n

Effectively, this means removing complexity from stylesheets, which are global and leaky, and moving that complexity to the templating system, which is much more isolated and easier to manage.

\n

I know that this approach has sped up the development process in my professional work and for small open source projects, and I’ve never seen any evidence that this would break down when working at scale. Though I suspect that very few have ever really attempted this, and it hasn’t been long enough to know what sort of problems this might cause over the long term.

\n

Is this semantic?

\n

Yes, read this article from Nicolas Gallagher — it is the best answer to this question on the Internet: About HTML Semantics and Front-End Architecture. Classes do not affect HTML semantics, and you absolutely should use well structured, accessible, semantic HTML. Period.

\n

What about mixing concerns?

\n

Web components and things like React already do this and for good reason. The CSS Zen Garden is a pipe dream. If you could actually redesign something by only editing the stylesheet, we would all be using the same template and no one would be writing any new HTML.

\n

Won’t this lead to a lot of classes in HTML?

\n

If you’re concerned about this, I’m concerned about how much repetition you have in your templates. Most web projects will be utilizing some sort of templating system. Just as with any code, templates should be kept DRY. If the markup for something like a modal overlay is defined multiple times across templates, it should be consolidated to a single place. Living style guides and using a component-based system like React can help enforce this approach across a team.

\n

Wouldn’t there be a huge maintenance cost?

\n

Potentially. Though, I’ve only seen a handful of similar techniques at scale, and I suspect that, if it were implemented in a sensible way, it would be far better than the technical debt we incur with current CSS practices.

\n

What about web components?

\n

Web components add a whole new dimension to this dynamic by introducing style encapsulation and composability — essentially reducing utility styles to an implementation detail. I’ll try to address what that could look like in a follow-up post.

\n

To be continued…

\n","tags":["css","variables","preprocessors","postprocessors","postcss"]} -------------------------------------------------------------------------------- /api/blog/the-advantages-of-table-views-over-left-nav-flyouts/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/the-advantages-of-table-views-over-left-nav-flyouts","slug":"the-advantages-of-table-views-over-left-nav-flyouts","title":"The Advantages of Table Views Over Left Nav Flyouts","description":"At first glance, these two navigation patterns seem very similar, but there are a few points that give table views a huge leg up. When dealing with a multi-tiered hierarchy or a variable amount of navigation items – such as text messages, emails, folksonomic categories, folders, files, etc. – table views can be a great solution. Lists are generally easier to scan than tiles, and vertical scrolling is an easy interaction for touchscreen mobile devices.","date":"2012-12-01T00:00:00.000Z","excerpt":"At first glance, these two navigation patterns seem very similar, but there are a few points that give table views a huge leg up. When dealing with a multi-tiered hierarchy or a variable amount of navigation items – such as text messages, emails, folksonomic categories, folders, files, etc. – table views can be a great solution. Lists are generally easier to scan than tiles, and vertical scrolling is an easy interaction for touchscreen mobile devices.","html":"

\"image\"

\n

At first glance, these two navigation patterns seem very similar, but there are a few points that give table views* a huge leg up. When dealing with a multi-tiered hierarchy or a variable amount of navigation items – such as text messages, emails, folksonomic categories, folders, files, etc. – table views can be a great solution. Lists are generally easier to scan than tiles, and vertical scrolling is an easy interaction for touchscreen mobile devices.

\n

Some advantages of table views:

\n

\"image\"

\n\n

\"image\"

\n\n

What can be learned from left nav flyouts

\n

One advantage that the left nav flyout pattern generally has over table views is the swipe-to-reveal gesture shortcut. For table views, this gesture could be used as a shortcut for the back button – much like Loren Brichter's original Twitter for iPad – and navigation is probably a more common action than deleting items with the swipe-to-delete gesture.

\n

\"image\"

\n

Some may argue that the modal nature of left nav flyouts is well-suited for use as filtering controls for large collections. But, to use the Apple Store app as an example again, table views can be a very efficient way to filter through large collections of information.

\n

Forget the flyout – use table views

\n

If your product has a complex IA that can't neatly fit into a tab bar, consider the advantages that table views can provide before jumping on the left nav flyout bandwagon.

\n

*Note: For lack of a better term, table views refers to the one-window drilldown pattern that combines lists and navigation bars and is widely used in iOS.

\n"} -------------------------------------------------------------------------------- /api/blog/the-cascade-is-not-inheritance/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/the-cascade-is-not-inheritance","slug":"the-cascade-is-not-inheritance","title":"The Cascade is Not Inheritance","description":"On more than one occasion, I've heard people conflate the cascade feature of CSS with inheritance.","date":"2019-07-20T00:00:00.000Z","excerpt":"On more than one occasion, I've heard people conflate the cascade feature of CSS with inheritance.","html":"

On more than one occasion, I've heard people conflate the cascade feature of CSS with inheritance.\nWhile it's an understandable thing to mix up because they are, in fact, related,\nI think it's important to use the correct terms when talking about technology like this.

\n

Inheritance

\n

Inheritance is when a child element inherits styles from one of its parent elements.\nSome (not all) CSS properties will automatically apply as a default value for child elements.\nThis allows you to set a font family and color at the top level of a page\nand have all elements within that page use the same styles.\nVery cool.

\n

The Cascade

\n

The Cascade is the set of rules that a browser uses to determine which particular styles should apply to a given element,\nwhen there are conflicting rules.\nThis is, in my mind, the trickiest part of the CSS language and it trips people up all the time.

\n

The algorithm specified in CSS takes the following into consideration when applying styles:

\n\n

This quote from MDN's Introduction to CSS: Cascade and Inheritance sums it up pretty well:

\n
\n

If you didn't fully understand the cascade, specificity, and inheritance, then don't worry! This is definitely the most complicated thing we've covered so far in the course, and is something that even professional web developers sometimes find tricky.

\n
\n"} -------------------------------------------------------------------------------- /api/blog/the-good-the-bad-and-the-cascade/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/the-good-the-bad-and-the-cascade","slug":"the-good-the-bad-and-the-cascade","title":"The Good, The Bad, and The Cascade","description":null,"date":"2019-03-17T00:00:00.000Z","excerpt":"\n

Abstractions

\n\n

Standards

\n\n

More bad stuff

\n\n

New Abstractions

\n\n","html":"\n

Abstractions

\n\n

Standards

\n\n

More bad stuff

\n\n

New Abstractions

\n\n","draft":true} -------------------------------------------------------------------------------- /api/blog/the-mystery-of-the-mustard-people/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/the-mystery-of-the-mustard-people","slug":"the-mystery-of-the-mustard-people","title":"The Mystery of the Mustard People","description":null,"date":"2023-08-21T00:00:00.000Z","excerpt":"

One of the more perplexing bugs I encountered working on Novantica so far was when all the NPC extras in the game suddenly turned a greenish-yellow color.\nI had just swapped out URP materials for ones using a new shader, so I suspected that change made this happen,\nbut I didn't want to revert back to the old materials.\nTo add icing to the cake, this issue was only happening in builds, not in the editor.\nI started to dig in, but didn't realize it would take so long to track down.

\n","html":"

One of the more perplexing bugs I encountered working on Novantica so far was when all the NPC extras in the game suddenly turned a greenish-yellow color.\nI had just swapped out URP materials for ones using a new shader, so I suspected that change made this happen,\nbut I didn't want to revert back to the old materials.\nTo add icing to the cake, this issue was only happening in builds, not in the editor.\nI started to dig in, but didn't realize it would take so long to track down.

\n\n

\"Screenshot

\n

At first, since I didn't know how to reproduce the issue in the editor,\nI tried swapping different materials and shaders on one of the character models to see if it was related to any prefabs used to spawn the extras.\nNo luck, the character was still yellow.\nAfter a bit of fruitless searching on the Unity forums,\nI tried moving the model to different scenes and sometimes the material worked and other times not.\nI also noticed that the NPCs that were loaded from additive scenes didn't suffer the same ailment, as seen in the screenshot above.\nThose scenes were Addressables, so I started to suspect it was something related to how I'd set up shared materials across scenes with the Addressables system.

\n
\n Screenshot of Unity Addressables editor options\n
\n Changing this option to Use Existing Build made the issue reproducible in the editor\n
\n
\n

I switched the Unity Addressables Play Mode Script option to Use Existing Build and was able to start reproducing the issue in the editor.\nIt felt like I was onto something.

\n

\"\"

\n

The next thing I tried was adding all the game's materials and textures to a new Addressables group.\nThis seemed to fix the yellow people problem, but now the materials were missing from some of the additively loaded Addressable scenes.\nIf you're familiar with Unity, the magenta colored objects in the screenshot above mean that the materials are missing.\nI tried a few different ways of grouping the materials in Addressables, but still wasn't able to get everything working as expected.

\n

At this point, I went outside to take a walk and clear my head.\nWhy were the characters turning yellow?\nThey should be rendering in magenta if the material was missing.\nThe shader looked like it was supposed to but only the color was off...

\n

Then it dawned on me.\nWhen I got back to the computer, I swapped the texture on the character material to confirm my suspicion.\nThe material was, in fact, working, but the UV maps were wrong.\nBut why?

\n
\n The texture used to color the low poly character models\n
\n The texture used to color the low poly character models.\n
\n
\n

This time, I found some useful hints from the forums.\nIt turns out the default Optimize Mesh Data setting on the imported character models was causing the UV map to get lost somewhere along the way.\nAs you can see in the texture above, the UV map controls how the low poly models are colored.\nFor some reason, the UV map was only set for one of these colors.

\n
\n \n
\n The default setting for Optimize Mesh was the culprit all along\n
\n
\n

After disabling this on all the imported FBX models, everything was rendering as expected again.\nCase closed.

\n

If you've run into similar issues with Unity, I hope this post was helpful,\nbut if you know of another or better way to avoid this issue, please let me know.

\n

Alternative titles considered for this post:

\n\n","tags":["devlog","unity","materials","shaders"]} -------------------------------------------------------------------------------- /api/blog/unit-testing-ui/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/unit-testing-ui","slug":"unit-testing-ui","title":"Unit testing UI","description":null,"date":"2016-06-20T00:00:00.000Z","excerpt":"

I recently read\nthis excellent article,\nwhere the design team at Vox has devised a testing framework for new UI components introduced into their pattern library.\nWhile the methods they suggest are excellent, and what I’d consider something that should be industry-standard in our field,\nit got me thinking that this concept could be taken a step further.\nWhat if designers wrote actual unit tests for UI components?\nWhat if those tests were actually applied in user acceptance testing, A/B tests, and tested against performance metrics?

\n","html":"

I recently read\nthis excellent article,\nwhere the design team at Vox has devised a testing framework for new UI components introduced into their pattern library.\nWhile the methods they suggest are excellent, and what I’d consider something that should be industry-standard in our field,\nit got me thinking that this concept could be taken a step further.\nWhat if designers wrote actual unit tests for UI components?\nWhat if those tests were actually applied in user acceptance testing, A/B tests, and tested against performance metrics?

\n\n

What would a unit test look like?

\n

This is a really contrived example of what a UI unit test could look like:

\n
describe: CTA button\n  context: when a user sees the button\n    expect: Users should know that the button is clickable\n    expect: User should be able to click the button\n    expect: Conversions should be above 4%\n
\n

Taking a cue from test driven development, these tests should be written before any design work is started, and they should \"fail\" – because there’s no design yet.

\n

Any work that follows should be towards making those tests pass.

\n

You might notice that this looks suspiciously like a user story,\nbut I think this approach differs in a few ways.\nFirst, user stories attempt to capture the how and why in addition to the what.\nThese unit tests only concern themselves with what.\nSecondly, user stories are very much a part of Agile software development,\nwhereas these are simply meant to measure the design of UI components.

\n

“Running” tests

\n

Once you have a potential component, or even better, several potential components,\nyou’d be ready to start testing it against the unit tests previously written.\nThese tests shouldn’t overshadow the typical formats and methodologies of user testing, A/B testing, etc.\nInstead, they should be used as an analytical tool to gauge the results.

\n

If, when user testing a feature, the user doesn’t notice the button, there’s still more design work to do.

\n

If, after ramping up a new button component, A/B testing shows that conversion is lower than expected, there’s still more work to do.

\n

If users are bouncing at higher-than-anticipated rates because of slow page load, there’s still more work to be done.

\n

Now, I will admit that design work is a lot less black and white than software development can be, and that it might be easy to shoot yourself in the foot with an approach like this. If, for example, conversion never gets above 3.9%, and you spend 2 weeks designing and redesigning a button, there are probably some other variables affecting things, and there are better ways to prioritize your time.

\n

But taking this idea as an approach to problem solving, gauging the efficacy of a design solution, and setting up goals for a product, I think it’s a concept worth exploring.

\n"} -------------------------------------------------------------------------------- /api/blog/unity-inputsystem-detector/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/unity-inputsystem-detector","slug":"unity-inputsystem-detector","title":"Unity InputSystem Touchscreen Detector","description":null,"date":"2023-10-05T00:00:00.000Z","excerpt":"

When I first started working on Novantica, I was considering releasing on iOS in addition to Steam and used TestFlight to manage development builds on my phone.\nI've always intended for the game to work well with gamepads/controllers, but I also wanted a way to play test the game using an on-screen virtual controller.

\n

I decided to use the InputSystem package so that the game would support most gamepads and it includes a very easy way to build a custom on-screen controller with the OnScreenControl component.\nFor the iOS build of the game, a custom on-screen controller shows, and automatically hides when a controller is connected.\nIt took a bit of fiddling with the InputSystem to get this to work, and I'd like to share how I set this up.

\n

First, make sure you have the InputSystem installed and enabled in your project.\nFind or create some images assets to use for any joysticks or buttons you want to use for the on-screen controller, then create a UI canvas for the controller, adding and positioning the joystick and buttons to suit your needs.

\n

The controller I had in the iOS builds looked like this:

\n

[IMAGE]

\n

Next, create a new script for hiding and showing the on-screen virtual controller.

\n

[GIST]

\n

Add this script to a parent component and hook up the UI canvas in the inspector.\nTo test this out in the editor, enter play mode, and you should see the on-screen virtual controller.\nNow connect a gamepad to your computer; the virtual controller should disappear.\nDisconnecting the physical controller should make the on-screen virtual controller reappear.

\n","html":"

When I first started working on Novantica, I was considering releasing on iOS in addition to Steam and used TestFlight to manage development builds on my phone.\nI've always intended for the game to work well with gamepads/controllers, but I also wanted a way to play test the game using an on-screen virtual controller.

\n

I decided to use the InputSystem package so that the game would support most gamepads and it includes a very easy way to build a custom on-screen controller with the OnScreenControl component.\nFor the iOS build of the game, a custom on-screen controller shows, and automatically hides when a controller is connected.\nIt took a bit of fiddling with the InputSystem to get this to work, and I'd like to share how I set this up.

\n

First, make sure you have the InputSystem installed and enabled in your project.\nFind or create some images assets to use for any joysticks or buttons you want to use for the on-screen controller, then create a UI canvas for the controller, adding and positioning the joystick and buttons to suit your needs.

\n

The controller I had in the iOS builds looked like this:

\n

[IMAGE]

\n

Next, create a new script for hiding and showing the on-screen virtual controller.

\n

[GIST]

\n

Add this script to a parent component and hook up the UI canvas in the inspector.\nTo test this out in the editor, enter play mode, and you should see the on-screen virtual controller.\nNow connect a gamepad to your computer; the virtual controller should disappear.\nDisconnecting the physical controller should make the on-screen virtual controller reappear.

\n","draft":true,"tags":["devlog","unity","input","controller","mobile","touchscreen","tutorial"]} -------------------------------------------------------------------------------- /api/blog/zero-configuration-react-static-site-generator/index.json: -------------------------------------------------------------------------------- 1 | {"path":"blog/zero-configuration-react-static-site-generator","slug":"zero-configuration-react-static-site-generator","title":"Zero-Configuration React Static Site Generator","description":null,"date":"2016-02-15T00:00:00.000Z","excerpt":"

React is a great way to generate static HTML with a component-based UI.\nOne of the biggest hurdles to working with React is the amount of boilerplate and build configuration it takes to get going.\nI wanted to make it dead-simple to start building static pages with React\nand without the need to install tons of npm modules and configure webpack.

\n","html":"

React is a great way to generate static HTML with a component-based UI.\nOne of the biggest hurdles to working with React is the amount of boilerplate and build configuration it takes to get going.\nI wanted to make it dead-simple to start building static pages with React\nand without the need to install tons of npm modules and configure webpack.

\n\n

That’s where static-react comes in. Here’s out to get started.

\n

Update: As of version 3.2.0, static-react includes Babel presets and they do not need to be installed separately. This post has been updated to reflect those changes.

\n

1. Install Dependencies

\n
npm i -D react static-react\n
\n

The Babel presets are optional, but make working with React much nicer, in my opinion.

\n

2. Add a Run Script

\n

Add the following run script to package.json.

\n
\"scripts\": {\n  \"start\": \"static-react components/Root.js > index.html\"\n}\n
\n

3. Configure Babel (Optional) *

\n

Add the following to package.json.

\n

4. Add Components

\n

Create a components directory and a Root.js React component.

\n
// Root.js\nimport React from 'react'\n\nclass Root extends React.Component {\n  render () {\n    return <h1>Hello</h1>\n  }\n}\n\nexport default Root\n
\n

5. Build

\n
npm start\n
\n

Open index.html and you should see the rendered Root component.

\n

Building Out a Page

\n

You'll certainly want more than just a heading in the final page.\nBuild out some more components and run npm start to rebuild.

\n

Here is an example set of starter components:

\n
// Root.js\nimport React from 'react'\nimport Head from './Head'\nimport Header from './Header'\nimport Main from './Main'\nimport Footer from './Footer'\n\nclass Root extends React.Component {\n  render () {\n    return (\n      <html>\n        <Head title='Static React Demo' />\n        <body className='px3'>\n          <Header title='Hello' />\n          <Main />\n          <Footer />\n        </body>\n      </html>\n    )\n  }\n}\n\nexport default Root\n
\n
// Head.js\nimport React from 'react'\n\nconst Head = ({ title }) => (\n  <head>\n    <meta charSet='utf-8' />\n    <title>{title}</title>\n    <link href='https://npmcdn.com/basscss-basic@1.0.0/index.css' rel='stylesheet' />\n    <link href='https://npmcdn.com/basscss@8.0.0/css/basscss.min.css' rel='stylesheet' />\n    <meta name='viewport' content='width=device-width, initial-scale=1' />\n  </head>\n)\n\nexport default Head\n
\n
// Header.js\nimport React from 'react'\n\nconst Header = ({ title }) => (\n  <header className='py3'>\n    <h1>{title}</h1>\n  </header>\n)\n\nexport default Header\n
\n
// Main.js\nimport React from 'react'\n\nconst Main = () => (\n  <main className='py4'>\n    <img src='http://lorempixel.com/256/256/cats/'\n      alt='Kitten' />\n  </main>\n)\n\nexport default Main\n
\n
// Footer.js\nimport React from 'react'\n\nconst Footer = () => (\n  <footer className='py3'>\n    <p>© 2016 Brent Jackson</p>\n  </footer>\n)\n\nexport default Footer\n
\n

If you have any thoughts on or issues with the static-react module, please check out the repo on GitHub.

\n

\n * Technically, I guess this post should be titled Near-Zero-Configuration, but using Babel is completely optional.\n

\n","tags":["react","static-site-generator"]} -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | import { 2 | basename, 3 | extname, 4 | join 5 | } from 'node:path'; 6 | import { 7 | readdirSync, 8 | readFileSync, 9 | createWriteStream, 10 | writeFileSync, 11 | } from 'node:fs'; 12 | import { ensureDirSync } from 'fs-extra'; 13 | import { createElement as x } from 'react'; 14 | import { renderToStaticNodeStream } from 'react-dom/server'; 15 | import matter from 'gray-matter'; 16 | import { remark } from 'remark'; 17 | import remarkHTML from 'remark-html'; 18 | import remarkExcerpt from 'remark-excerpt'; 19 | import remarkGFM from "remark-gfm"; 20 | 21 | import Html from './src/Html.js'; 22 | import Home from './src/Home.js'; 23 | import NotFound from './src/404.js'; 24 | import Blog from './src/Blog.js'; 25 | import Post from './src/Post.js'; 26 | 27 | const config = { 28 | title: 'Jxnblk | Brent Jackson', 29 | description: 'Aspiring indie game developer, software engineer, and proud cat parent – he/him', 30 | }; 31 | 32 | async function parseFile (filename) { 33 | const slug = basename(filename, ".md"); 34 | const path = 'blog/' + slug; 35 | const raw = readFileSync(filename); // join('src', 'posts', filename)); 36 | const { content, data } = matter(raw); 37 | const vf = await remark() 38 | .use(remarkGFM) 39 | .use(remarkHTML, { sanitize: false }) 40 | .process(content); 41 | const html = String(vf); 42 | const evf = await remark() 43 | // .use(remarkGFM) 44 | .use(remarkExcerpt) 45 | .use(remarkHTML, { sanitize: false }) 46 | .process(content); 47 | const excerpt = String(evf); 48 | 49 | return { 50 | filename, 51 | path, 52 | slug, 53 | raw, 54 | html, 55 | excerpt, 56 | description: data.description || data.excerpt || null, 57 | ...data, 58 | content: Post({ ...data, html }) 59 | }; 60 | } 61 | 62 | async function getPages () { 63 | const mds = [ 64 | ...readdirSync('src/posts') 65 | .filter(f => extname(f) == '.md') 66 | .map(f => join('src', 'posts', f)), 67 | ...readdirSync('src/devlog') 68 | .filter(f => extname(f) == '.md') 69 | .map(f => join('src', 'devlog', f)), 70 | ]; 71 | const promises = mds.map(parseFile); 72 | // console.log(`Found ${mds.length} blog posts`); 73 | const posts = await Promise.all(promises); 74 | 75 | const pages = [ ...posts ]; 76 | 77 | pages.push({ 78 | path: 'blog', 79 | content: Blog({ posts }), 80 | }); 81 | 82 | pages.push({ 83 | path: '', 84 | content: Home({ posts }), 85 | }); 86 | pages.push({ 87 | path: '404', 88 | content: NotFound(), 89 | }) 90 | 91 | return pages; 92 | }; 93 | 94 | const pages = await getPages(); 95 | console.log(pages.length); 96 | 97 | pages.forEach(page => { 98 | if (page.path) ensureDirSync(page.path); 99 | let filename = join(page.path, 'index.html'); 100 | if (page.path == '404') { 101 | filename = '404.html'; 102 | }; 103 | const write = createWriteStream(filename); 104 | const stream = renderToStaticNodeStream(x(Html, page)); 105 | 106 | write.write('\n'); 107 | stream.pipe(write); 108 | 109 | write.on('finish', () => { 110 | console.log(page.path); 111 | }); 112 | }); 113 | 114 | // api 115 | // 116 | function pageToData ({ 117 | path, 118 | slug, 119 | title, 120 | description, 121 | date, 122 | excerpt, 123 | html, 124 | draft, 125 | tags, 126 | image, 127 | }) { 128 | return { 129 | path, 130 | slug, 131 | title, 132 | description, 133 | date, 134 | excerpt, 135 | html, 136 | draft, 137 | tags, 138 | image, 139 | }; 140 | } 141 | 142 | const exclude = ["", "blog", "404", "blog/notes"]; 143 | const posts = pages.filter(p => !exclude.includes(p.path)) 144 | .map(pageToData) 145 | .sort((a, b) => b.date - a.date); 146 | 147 | const index = posts.map(p => ({ 148 | path: p.path, 149 | slug: p.slug, 150 | title: p.title, 151 | date: p.date, 152 | excerpt: p.excerpt, 153 | draft: p.draft, 154 | tags: p.tags, 155 | })); 156 | 157 | const about = pageToData(await parseFile("about.md")); 158 | 159 | const all = { 160 | posts, 161 | about 162 | } 163 | 164 | const indexJSON = JSON.stringify(index); 165 | const allJSON = JSON.stringify(all); 166 | 167 | writeFileSync(join('api', 'index.json'), indexJSON); 168 | writeFileSync(join('api', 'all.json'), allJSON); 169 | 170 | posts.forEach(post => { 171 | ensureDirSync(join('api', post.path)); 172 | const json = JSON.stringify(post); 173 | writeFileSync(join('api', post.path, 'index.json'), json); 174 | }); 175 | 176 | 177 | -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/favicon.png -------------------------------------------------------------------------------- /images/arrow-box.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/avatar-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/avatar.png -------------------------------------------------------------------------------- /images/avatar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/bot-term.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/brent-jackson.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/brent-jackson.jpg -------------------------------------------------------------------------------- /images/bubo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /images/color.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/conveyor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /images/design-graph-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/design-graph-1.png -------------------------------------------------------------------------------- /images/design-graph-1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/design-graph-1.webp -------------------------------------------------------------------------------- /images/design-graph-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/design-graph-2.png -------------------------------------------------------------------------------- /images/design-graph-2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/design-graph-2.webp -------------------------------------------------------------------------------- /images/design-graph-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/design-graph-3.png -------------------------------------------------------------------------------- /images/design-graph-3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/design-graph-3.webp -------------------------------------------------------------------------------- /images/design-graph-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/design-graph-4.png -------------------------------------------------------------------------------- /images/design-graph-4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/design-graph-4.webp -------------------------------------------------------------------------------- /images/double-helix.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /images/hexagon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/isolated-interior/devlog-04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/isolated-interior/devlog-04.jpg -------------------------------------------------------------------------------- /images/isolated-interior/isolated-interior-inside.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/isolated-interior/isolated-interior-inside.jpg -------------------------------------------------------------------------------- /images/isolated-interior/isolated-interior-outside.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/isolated-interior/isolated-interior-outside.jpg -------------------------------------------------------------------------------- /images/magplate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /images/modular-npcs/blender-parts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/blender-parts.jpg -------------------------------------------------------------------------------- /images/modular-npcs/early-aries.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/early-aries.jpg -------------------------------------------------------------------------------- /images/modular-npcs/early-sketches.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/early-sketches.jpg -------------------------------------------------------------------------------- /images/modular-npcs/game-objects.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/game-objects.jpg -------------------------------------------------------------------------------- /images/modular-npcs/in-game-crowd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/in-game-crowd.jpg -------------------------------------------------------------------------------- /images/modular-npcs/in-game-crowd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/in-game-crowd.webp -------------------------------------------------------------------------------- /images/modular-npcs/main-characters-before-after.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/main-characters-before-after.jpg -------------------------------------------------------------------------------- /images/modular-npcs/mixamo-animation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/mixamo-animation.jpg -------------------------------------------------------------------------------- /images/modular-npcs/mixamo-rigging.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/mixamo-rigging.jpg -------------------------------------------------------------------------------- /images/modular-npcs/modular-characters.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/modular-characters.jpg -------------------------------------------------------------------------------- /images/modular-npcs/modular-npc-walk.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/modular-npc-walk.mp4 -------------------------------------------------------------------------------- /images/modular-npcs/old-extras.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/old-extras.jpg -------------------------------------------------------------------------------- /images/modular-npcs/textures.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/textures.jpg -------------------------------------------------------------------------------- /images/modular-npcs/unity-scripts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/modular-npcs/unity-scripts.jpg -------------------------------------------------------------------------------- /images/mustard/addressables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/addressables.png -------------------------------------------------------------------------------- /images/mustard/bank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/bank.png -------------------------------------------------------------------------------- /images/mustard/bench.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/bench.jpg -------------------------------------------------------------------------------- /images/mustard/benches.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/benches.jpg -------------------------------------------------------------------------------- /images/mustard/character-texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/character-texture.png -------------------------------------------------------------------------------- /images/mustard/coffee.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/coffee.jpg -------------------------------------------------------------------------------- /images/mustard/hotel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/hotel.jpg -------------------------------------------------------------------------------- /images/mustard/hotel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/hotel.png -------------------------------------------------------------------------------- /images/mustard/optimize-mesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/optimize-mesh.png -------------------------------------------------------------------------------- /images/mustard/pizza.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/pizza.jpg -------------------------------------------------------------------------------- /images/mustard/street.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/street.jpg -------------------------------------------------------------------------------- /images/mustard/tacos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/tacos.png -------------------------------------------------------------------------------- /images/mustard/techshop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/techshop.png -------------------------------------------------------------------------------- /images/mustard/temple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/mustard/temple.png -------------------------------------------------------------------------------- /images/novantica-promo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jxnblk/blog/b06ed654f7991274c15c73275c0a15721923730c/images/novantica-promo.jpg -------------------------------------------------------------------------------- /images/stripe.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "jxnblog", 4 | "version": "5.0.0", 5 | "description": "The writing of Brent Jackson", 6 | "type": "module", 7 | "scripts": { 8 | "start": "nodemon -e js,md,json --loader=import-jsx ./build.js", 9 | "build": "node --loader=import-jsx ./build.js", 10 | "serve": "http-server -c-1", 11 | "test": "node --loader=import-jsx ./build.js" 12 | }, 13 | "author": "Brent Jackson", 14 | "license": "MIT", 15 | "dependencies": { 16 | "fs-extra": "^11.1.1", 17 | "gray-matter": "^4.0.3", 18 | "http-server": "^14.1.1", 19 | "import-jsx": "^5.0.0", 20 | "nodemon": "^3.0.1", 21 | "react": "^18.2.0", 22 | "react-dom": "^18.2.0", 23 | "remark": "^15.0.1", 24 | "remark-excerpt": "^1.0.0-beta.1", 25 | "remark-gfm": "^4.0.0", 26 | "remark-html": "^16.0.1", 27 | "remark-parse": "^11.0.0", 28 | "unified": "^11.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /principles/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Principles of Web Design 3 | date: 2014-03-24 4 | --- 5 | 6 | 7 | # Good web design is 8 | 9 | ## Universally Accessible 10 | 11 | Everyone should be able to view and use the web. Don’t exclude people by making bad decisions. 12 | 13 | ## Simple 14 | 15 | Keep it simple stupid. The mother of all design principles still applies to the web. 16 | 17 | ## Familiar 18 | 19 | The web is built on patterns. Rely on familiarity and avoid breaking expectations. 20 | 21 | ## Tested 22 | 23 | Data is a powerful tool, but just like any tool, it can be abused. Gather and utilize qualitative and quantitative data and constantly strive to improve processes. 24 | 25 | ## Iterative 26 | 27 | The web is an ever-evolving platform, and web design is never finished. 28 | 29 | ## Unobtrusive 30 | 31 | The web is about information. Make it easy to find and stay out of the way. 32 | 33 | ## Fast 34 | 35 | The web thrives on the speed of information. Make things faster not slower. 36 | 37 | ## As little design as possible 38 | 39 | And as little code as possible. 40 | 41 | --- 42 | 43 | *Originally posted at jxnblk.com/principles* 44 | 45 | Infinitely inspired by Dieter Rams: 46 | https://www.vitsoe.com/us/about/good-design 47 | 48 | -------------------------------------------------------------------------------- /src/404.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Header from './Header.js'; 3 | import Footer from './Footer.js'; 4 | 5 | export default function () { 6 | return ( 7 | <> 8 |
9 |
10 |
11 | ERR 404 12 |
13 |
14 |