├── babel.config.js ├── .prettierignore ├── .gitignore ├── lerna.json ├── prettier.config.js ├── docs ├── vercel.json ├── next.config.js ├── pages │ ├── _app.js │ ├── _document.js │ └── index.js ├── package.json ├── components │ └── color-switcher.js └── yarn.lock ├── .github └── workflows │ ├── test.yml │ └── format.yml ├── packages ├── theme │ ├── fonts │ │ ├── reg.css │ │ ├── reg-bold.css │ │ └── reg-ital-bold.css │ ├── src │ │ ├── prism.ts │ │ ├── overrides.d.ts │ │ └── index.ts │ ├── package.json │ ├── README.md │ └── tsconfig.json └── meta │ ├── package.json │ ├── README.md │ ├── test │ ├── index.js │ └── __snapshots__ │ │ └── index.js.snap │ └── src │ └── index.js ├── README.md ├── package.json ├── LICENSE.md └── CONTRIBUTING.md /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@babel/env', '@babel/react'] 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | public 2 | package-lock.json 3 | yarn.lock 4 | node_modules 5 | dist 6 | coverage 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .next 2 | public 3 | package-lock.json 4 | node_modules 5 | dist 6 | coverage 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0-alpha.0", 3 | "npmClient": "yarn", 4 | "useWorkspaces": true, 5 | "packages": ["packages/*"] 6 | } 7 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | trailingComma: 'none', 4 | arrowParens: 'avoid', 5 | printWidth: 80, 6 | semi: false 7 | } 8 | -------------------------------------------------------------------------------- /docs/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "public": true, 3 | "trailingSlash": true, 4 | "github": { "silent": true }, 5 | "redirects": [ 6 | { "source": "/gh/", "destination": "https://github.com/hackclub/theme" } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /docs/next.config.js: -------------------------------------------------------------------------------- 1 | const isProd = process.env.NODE_ENV === 'production' 2 | const withMDX = require('@next/mdx')({ extension: /\.mdx?$/ }) 3 | module.exports = withMDX({ 4 | pageExtensions: ['js', 'jsx', 'mdx'], 5 | assetPrefix: isProd ? 'https://theme.hackclub.com' : '' 6 | }) 7 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | jest: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: actions/setup-node@v3 13 | - run: yarn install 14 | - run: yarn run test 15 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: format 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | prettier: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: actions/setup-node@v3 13 | - run: yarn install 14 | - run: yarn run checkFormat 15 | -------------------------------------------------------------------------------- /packages/theme/fonts/reg.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Phantom Sans'; 3 | src: 4 | url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Regular.woff') 5 | format('woff'), 6 | url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Regular.woff2') 7 | format('woff2'); 8 | font-weight: normal; 9 | font-style: normal; 10 | font-display: swap; 11 | } 12 | -------------------------------------------------------------------------------- /docs/pages/_app.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import NextApp from 'next/app' 3 | 4 | import '@hackclub/theme/fonts/reg-ital-bold.css' 5 | import theme from '@hackclub/theme' 6 | import { ThemeProvider } from 'theme-ui' 7 | 8 | export default class App extends NextApp { 9 | render() { 10 | const { Component, pageProps } = this.props 11 | return ( 12 | 13 | 14 | 15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/theme/src/prism.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | '.comment,.prolog,.doctype,.cdata,.punctuation,.operator,.entity,.url': { 3 | color: 'muted' 4 | }, 5 | '.comment': { 6 | fontStyle: 'italic' 7 | }, 8 | '.property, .tag, .boolean, .number, .constant, .symbol, .deleted, .function, .class-name, .regex, .important, .variable': 9 | { 10 | color: 'red' 11 | }, 12 | '.atrule, .attr-value, .keyword': { 13 | color: 'blue' 14 | }, 15 | '.selector, .attr-name, .string, .char, .builtin, .inserted': { 16 | color: 'orange' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docs/pages/_document.js: -------------------------------------------------------------------------------- 1 | import Document, { Html, Head, Main, NextScript } from 'next/document' 2 | import { InitializeColorMode } from 'theme-ui' 3 | 4 | export default class extends Document { 5 | static async getInitialProps(ctx) { 6 | const initialProps = await Document.getInitialProps(ctx) 7 | return { ...initialProps } 8 | } 9 | 10 | render() { 11 | return ( 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | ) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hackclub/theme-docs", 3 | "version": "0.0.1", 4 | "author": "Lachlan Campbell (https://lachlanjc.me) ", 5 | "license": "MIT", 6 | "private": true, 7 | "scripts": { 8 | "dev": "next", 9 | "build": "next build", 10 | "start": "yarn run dev" 11 | }, 12 | "dependencies": { 13 | "@hackclub/meta": "^1.0.0", 14 | "@hackclub/theme": "^0.3.1", 15 | "@mdx-js/loader": "^1.6.21", 16 | "@next/mdx": "^10.0.1", 17 | "@theme-ui/style-guide": "^0.3.3", 18 | "lodash": "^4.17.21", 19 | "next": "^12.1.0", 20 | "react": "^17.0.1", 21 | "react-dom": "^17.0.1", 22 | "theme-ui": "^0.3.3" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/theme/fonts/reg-bold.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Phantom Sans'; 3 | src: 4 | url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Regular.woff') 5 | format('woff'), 6 | url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Regular.woff2') 7 | format('woff2'); 8 | font-weight: normal; 9 | font-style: normal; 10 | font-display: swap; 11 | } 12 | @font-face { 13 | font-family: 'Phantom Sans'; 14 | src: 15 | url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Bold.woff') 16 | format('woff'), 17 | url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Bold.woff2') 18 | format('woff2'); 19 | font-weight: bold; 20 | font-style: normal; 21 | font-display: swap; 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hack Club Theme 2 | 3 | ![format](https://github.com/hackclub/theme/workflows/format/badge.svg) 4 | ![test](https://github.com/hackclub/theme/workflows/test/badge.svg) 5 | 6 | [Hack Club](https://hackclub.com)’s frontend design system/tools, 7 | made for [Theme UI](https://theme-ui.com). An alternative for CSS 8 | projects is [available here](https://github.com/hackclub/css). 9 | 10 | > For getting started, check out [theme-starter](https://github.com/hackclub/theme-starter)! 11 | 12 | ## Packages 13 | 14 | 1. `@hackclub/theme` – Theme UI base theme 15 | 2. `@hackclub/meta` – React component for generating social tags for `` 16 | 17 | ## Docs 18 | 19 | The docs site source is in `/docs`. It is made with Next.js & deployed on Vercel. 20 | 21 | [**theme.hackclub.com**](https://theme.hackclub.com) 22 | -------------------------------------------------------------------------------- /packages/theme/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hackclub/theme", 3 | "description": "Hack Club’s theme for Theme UI websites", 4 | "version": "0.3.3", 5 | "author": "Lachlan Campbell (https://lachlanjc.com)", 6 | "source": "src/index.ts", 7 | "main": "dist/index.js", 8 | "module": "dist/index.esm.js", 9 | "unpkg": "dist/index.umd.js", 10 | "types": "dist/index.d.ts", 11 | "sideEffects": false, 12 | "scripts": { 13 | "prepare": "microbundle", 14 | "watch": "microbundle watch --no-compress" 15 | }, 16 | "publishConfig": { 17 | "access": "public" 18 | }, 19 | "license": "MIT", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/hackclub/theme.git", 23 | "directory": "packages/theme" 24 | }, 25 | "gitHead": "029e83b336da014076112a68048dfa65565013ee", 26 | "dependencies": { 27 | "theme-ui": "^0.14.5" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "prepare": "lerna run prepare", 5 | "clean": "lerna run clean", 6 | "test": "jest", 7 | "format": "prettier --write .", 8 | "checkFormat": "prettier --check ." 9 | }, 10 | "workspaces": { 11 | "packages": [ 12 | "packages/*" 13 | ] 14 | }, 15 | "devDependencies": { 16 | "@babel/cli": "^7.17.10", 17 | "@babel/core": "^7.18.2", 18 | "@babel/preset-env": "^7.18.2", 19 | "@babel/preset-react": "^7.17.12", 20 | "@testing-library/react": "^11.1.2", 21 | "babel-jest": "^26.6.3", 22 | "jest": "^26.6.3", 23 | "lerna": "^3.22.1", 24 | "microbundle": "^0.12.4", 25 | "prettier": "2.6.2" 26 | }, 27 | "jest": { 28 | "testMatch": [ 29 | "**/packages/**/test/*.js" 30 | ], 31 | "testPathIgnorePatterns": [ 32 | "/node_modules/" 33 | ] 34 | }, 35 | "dependencies": {} 36 | } 37 | -------------------------------------------------------------------------------- /packages/meta/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hackclub/meta", 3 | "description": "Generate meta tags for Hack Club pages", 4 | "version": "1.1.33", 5 | "source": "src/index.js", 6 | "main": "dist/index.js", 7 | "module": "dist/index.esm.js", 8 | "unpkg": "dist/index.umd.js", 9 | "sideEffects": false, 10 | "scripts": { 11 | "prepare": "rm -rf ./dist && microbundle --jsx React.createElement", 12 | "watch": "microbundle watch --jsx React.createElement --no-compress" 13 | }, 14 | "publishConfig": { 15 | "access": "public" 16 | }, 17 | "license": "MIT", 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/hackclub/theme.git", 21 | "directory": "packages/meta" 22 | }, 23 | "peerDependencies": { 24 | "react": "^18.0.0" 25 | }, 26 | "devDependencies": { 27 | "react": "^18.0.0", 28 | "react-dom": "^18.0.0" 29 | }, 30 | "gitHead": "029e83b336da014076112a68048dfa65565013ee" 31 | } 32 | -------------------------------------------------------------------------------- /packages/theme/src/overrides.d.ts: -------------------------------------------------------------------------------- 1 | import 'theme-ui' 2 | 3 | declare module 'theme-ui' { 4 | interface Theme { 5 | util: { 6 | /** 7 | * '@media (prefers-reduced-motion: no-preference)' 8 | */ 9 | motion: string 10 | /** 11 | * '@media (prefers-reduced-motion: reduce)' 12 | */ 13 | reduceMotion: string 14 | /** 15 | * '@media (prefers-reduced-transparency: reduce)' 16 | */ 17 | reduceTransparency: string 18 | /** 19 | * '@supports (-webkit-background-clip: text)' 20 | */ 21 | supportsClipText: string 22 | /** 23 | * '@supports (-webkit-backdrop-filter: none) or (backdrop-filter: none)' 24 | */ 25 | supportsBackdrop: string 26 | cx: (c: string) => ColorOrNestedColorScale 27 | gx: (from: string, to: string) => ThemeUICSSObject 28 | gxText: (from: string, to: string) => ThemeUICSSObject 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/theme/README.md: -------------------------------------------------------------------------------- 1 | # @hackclub/theme 2 | 3 | [Theme UI](https://theme-ui.com) theme for [Hack Club](https://hackclub.com) 4 | 5 | ```bash 6 | yarn add @hackclub/theme 7 | # npm i @hackclub/theme 8 | ``` 9 | 10 | Check out theme: [**theme.hackclub.com**](https://theme.hackclub.com) 11 | 12 | ## Usage 13 | 14 | ```js 15 | import { ThemeProvider } from 'theme-ui' 16 | import theme from '@hackclub/theme' 17 | 18 | export default ({ children }) => ( 19 | {children} 20 | ) 21 | ``` 22 | 23 | ### Fonts 24 | 25 | This package also bundles 3 CSS files that include Hack Club’s webfonts: 26 | `reg.css`, `reg-bold.css`, & `reg-ital-bold.css`. 27 | To use, just import straight from the package: 28 | 29 | ```js 30 | import '@hackclub/theme/fonts/reg-bold.css' 31 | ``` 32 | 33 | (In a Next.js project, [place](https://nextjs.org/docs/basic-features/built-in-css-support) in the `pages/_app.js` file.) 34 | 35 | MIT License 36 | -------------------------------------------------------------------------------- /packages/meta/README.md: -------------------------------------------------------------------------------- 1 | # @hackclub/meta 2 | 3 | React component for generating Open Graph/etc meta tags for Hack Club pages. 4 | Designed for Next.js but framework-agnostic. 5 | 6 | Not recommended for non-Hack Club sites—you’ll get Hack Club favicons :) 7 | 8 | ## Usage 9 | 10 | ```bash 11 | yarn add @hackclub/meta 12 | # npm i @hackclub/meta 13 | ``` 14 | 15 | Example for [hackathons.hackclub.com](https://hackathons.hackclub.com): 16 | 17 | ```js 18 | // import Head from 'next/head' 19 | 20 | 29 | ``` 30 | 31 | All props are optional. If you include multiple times, the tags from the last 32 | instance will be used. 33 | 34 | MIT License 35 | -------------------------------------------------------------------------------- /docs/components/color-switcher.js: -------------------------------------------------------------------------------- 1 | import { IconButton, useColorMode } from 'theme-ui' 2 | 3 | const ColorSwitcher = props => { 4 | const [mode, setMode] = useColorMode() 5 | return ( 6 | setMode(mode === 'dark' ? 'light' : 'dark')} 8 | title="Invert Colors" 9 | sx={{ 10 | position: 'absolute', 11 | top: 3, 12 | right: 3, 13 | color: 'primary', 14 | borderRadius: 'circle', 15 | transition: 'box-shadow .125s ease-in-out', 16 | ':hover,:focus': { 17 | boxShadow: '0 0 0 2px', 18 | outline: 'none' 19 | } 20 | }} 21 | > 22 | 23 | 31 | 32 | 33 | 34 | ) 35 | } 36 | 37 | export default ColorSwitcher 38 | -------------------------------------------------------------------------------- /packages/theme/fonts/reg-ital-bold.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Phantom Sans'; 3 | src: 4 | url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Regular.woff') 5 | format('woff'), 6 | url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Regular.woff2') 7 | format('woff2'); 8 | font-weight: normal; 9 | font-style: normal; 10 | font-display: swap; 11 | } 12 | @font-face { 13 | font-family: 'Phantom Sans'; 14 | src: 15 | url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Italic.woff') 16 | format('woff'), 17 | url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Italic.woff2') 18 | format('woff2'); 19 | font-weight: normal; 20 | font-style: italic; 21 | font-display: swap; 22 | } 23 | @font-face { 24 | font-family: 'Phantom Sans'; 25 | src: 26 | url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Bold.woff') 27 | format('woff'), 28 | url('https://assets.hackclub.com/fonts/Phantom_Sans_0.7/Bold.woff2') 29 | format('woff2'); 30 | font-weight: bold; 31 | font-style: normal; 32 | font-display: swap; 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2020 The Hack Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /packages/theme/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "ES2018" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 5 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 6 | "declaration": true /* Generates corresponding '.d.ts' file. */, 7 | "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, 8 | "sourceMap": true /* Generates corresponding '.map' file. */, 9 | "outDir": "./dist" /* Redirect output structure to the directory. */, 10 | "removeComments": true /* Do not emit comments to output. */, 11 | 12 | /* Module Resolution Options */ 13 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 14 | 15 | /* Advanced Options */ 16 | "skipLibCheck": true /* Skip type checking of declaration files. */, 17 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 18 | }, 19 | "include": ["src"] 20 | } 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Hey—thanks for your interest in contributing! We follow the [Hack Club Code of Conduct](https://hackclub.com/conduct/), so please be kind and reach out if you have any questions or concerns. 4 | 5 | ## Local Development 6 | 7 | This repo uses [Yarn Workspaces][] and [Lerna][] to develop multiple packages together as a monorepo. 8 | Be sure to install [Yarn][] before setting up the development environment. 9 | 10 | Install dependencies and link local packages in the root directory: 11 | 12 | ```bash 13 | yarn 14 | ``` 15 | 16 | After yarn has linked packages and installed dependences in the repo, you can run whatever you’re looking for. 17 | 18 | ```bash 19 | yarn workspace 20 | ``` 21 | 22 | Where name of package is something like `@hackclub/theme` or `@hackclub/meta` (one of the packages 23 | listed by yarn when you run the `yarn workspaces info` command) 24 | 25 | Example: 26 | 27 | ```bash 28 | yarn workspace @hackclub/meta prepare 29 | ``` 30 | 31 | ## Tests 32 | 33 | Unit tests are run with [Jest][], and each relevant package includes a `test/` directory with unit tests for that package. 34 | 35 | Running tests: 36 | 37 | ```sh 38 | yarn test 39 | ``` 40 | 41 | Running tests in watch mode: 42 | 43 | ```sh 44 | yarn test --watch 45 | ``` 46 | 47 | ## Pull Requests 48 | 49 | When opening a pull request, please be sure to update any relevant documentation. 50 | 51 | --- 52 | 53 | _Doc adapted from [Theme UI][]_ 54 | 55 | [yarn]: https://yarnpkg.com 56 | [yarn workspaces]: https://yarnpkg.com/en/docs/workspaces 57 | [lerna]: https://github.com/lerna/lerna 58 | [jest]: https://jestjs.io/ 59 | [theme ui]: https://github.com/system-ui/theme-ui/blob/master/CONTRIBUTING.md 60 | -------------------------------------------------------------------------------- /packages/meta/test/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render, cleanup } from '@testing-library/react' 3 | import Meta from '../src' 4 | 5 | afterEach(cleanup) 6 | 7 | test('Meta renders', () => { 8 | const { container, getByText } = render() 9 | expect(getByText('Hack Club')).toBeTruthy() 10 | expect( 11 | container.querySelector('[property="og:title"][content="Hack Club"]') 12 | ).toBeTruthy() 13 | expect( 14 | container.querySelector('[name="twitter:title"][content="Hack Club"]') 15 | ).toBeTruthy() 16 | expect(container).toMatchSnapshot() 17 | }) 18 | 19 | test('Meta renders custom title', () => { 20 | const title = 'Custom Title – Hack Club' 21 | const { container, getByText } = render() 22 | expect(getByText(title)).toBeTruthy() 23 | expect( 24 | container.querySelector(`[property="og:title"][content="${title}"]`) 25 | ).toBeTruthy() 26 | expect( 27 | container.querySelector(`[name="twitter:title"][content="${title}"]`) 28 | ).toBeTruthy() 29 | expect(container).toMatchSnapshot() 30 | }) 31 | 32 | test('Meta renders image', () => { 33 | const url = 'https://hackclub.com/cards/bank.jpg' 34 | const { container } = render() 35 | expect( 36 | container.querySelector( 37 | 'meta[name="twitter:card"][content="summary_large_image"]' 38 | ) 39 | ).toBeTruthy() 40 | expect(container.querySelectorAll(`[content="${url}"]`)).toHaveLength(2) 41 | expect(container).toMatchSnapshot() 42 | }) 43 | 44 | test('Meta renders custom color', () => { 45 | const color = '#0069ff' 46 | const { container } = render() 47 | expect( 48 | container.querySelector(`meta[name="theme-color"][content="${color}"]`) 49 | ).toBeTruthy() 50 | expect(container).toMatchSnapshot() 51 | }) 52 | 53 | test('Meta renders children', () => { 54 | const { container } = render( 55 | 56 | 57 | 58 | ) 59 | expect( 60 | container.querySelector('meta[name="children"][content="present"]') 61 | ).toBeTruthy() 62 | expect(container).toMatchSnapshot() 63 | }) 64 | -------------------------------------------------------------------------------- /packages/meta/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const makeTitle = (title, name) => 4 | title === name ? title : `${title} – ${name}` 5 | 6 | const Meta = ({ 7 | as: ElementType = React.Fragment, 8 | name = 'Hack Club', // site name 9 | title = 'Hack Club', // page title 10 | description, 11 | image, 12 | color = '#ec3750', 13 | manifest = 'https://assets.hackclub.com/favicons/site.webmanifest', 14 | children 15 | }) => ( 16 | 17 | 18 | 19 | 20 | 21 | {makeTitle(title, name)} 22 | 23 | 28 | {description && ( 29 | 30 | 31 | 32 | 33 | 34 | )} 35 | {image && ( 36 | 37 | 38 | 39 | 40 | 41 | )} 42 | 43 | 44 | 50 | 51 | 57 | 63 | 69 | 75 | {manifest && } 76 | {children} 77 | 78 | ) 79 | 80 | export default Meta 81 | -------------------------------------------------------------------------------- /packages/meta/test/__snapshots__/index.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Meta renders 1`] = ` 4 |
5 | 9 | 13 | 17 | 21 | 22 | Hack Club 23 | 24 | 28 | 32 | 36 | 40 | 45 | 50 | 56 | 62 | 66 |
67 | `; 68 | 69 | exports[`Meta renders children 1`] = ` 70 |
71 | 75 | 79 | 83 | 87 | 88 | Hack Club 89 | 90 | 94 | 98 | 102 | 106 | 111 | 116 | 122 | 128 | 132 | 136 |
137 | `; 138 | 139 | exports[`Meta renders custom color 1`] = ` 140 |
141 | 145 | 149 | 153 | 157 | 158 | Hack Club 159 | 160 | 164 | 168 | 172 | 176 | 181 | 186 | 192 | 198 | 202 |
203 | `; 204 | 205 | exports[`Meta renders custom title 1`] = ` 206 |
207 | 211 | 215 | 219 | 223 | 224 | Custom Title – Hack Club 225 | 226 | 230 | 234 | 238 | 242 | 247 | 252 | 258 | 264 | 268 |
269 | `; 270 | 271 | exports[`Meta renders image 1`] = ` 272 |
273 | 277 | 281 | 285 | 289 | 290 | Hack Club 291 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | 320 | 325 | 330 | 336 | 342 | 346 |
347 | `; 348 | -------------------------------------------------------------------------------- /docs/pages/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | BaseStyles, 3 | Badge, 4 | Box, 5 | Button, 6 | Card, 7 | Checkbox, 8 | Container, 9 | Flex, 10 | Grid, 11 | Heading, 12 | Input, 13 | Label, 14 | Link, 15 | NavLink, 16 | Radio, 17 | Select, 18 | Slider, 19 | Text, 20 | Textarea 21 | } from 'theme-ui' 22 | import Head from 'next/head' 23 | import Meta from '@hackclub/meta' 24 | import theme from '@hackclub/theme' 25 | import ColorSwitcher from '../components/color-switcher' 26 | import { TypeScale, ColorPalette } from '@theme-ui/style-guide' 27 | 28 | const DocsPage = () => ( 29 | <> 30 | 31 | Hack Club Theme 32 | 36 | 37 | 38 | 39 | 40 | 41 | Hack Club Theme 42 | 43 | 44 | Hack Club’s theme + React 45 | components for Theme UI. 46 | 47 | 59 | GitHub 60 | 61 | NPM 62 | 63 | 64 | Starter 65 | 66 | 67 | 68 | 69 | 70 | 71 | Containers 72 | 73 | {Object.keys(theme.layout).map(key => ( 74 | 85 | {key} 86 | 87 | ))} 88 | 89 | 93 | 94 | Text 95 | 96 | {Object.keys(theme.text).map(key => { 97 | const Component = key.includes('head') ? Heading : Text 98 | return ( 99 | 100 | {key} 101 | 102 | ) 103 | })} 104 | 105 | 106 |

107 | This is a whole paragraph of text, include{' '} 108 | code like this, as well as{' '} 109 | 110 | linked code 111 | 112 | {' & '} 113 | regular links. The paragraph 114 | ended up being 1 sentence, but now I guess it’s two 115 | . 116 |

117 |
118 |             Here’s a code block! No highlighting to be found.
119 |           
120 |
121 | Buttons 122 | {Object.keys(theme.buttons).map(key => ( 123 | 126 | ))} 127 | Cards 128 | 133 | {Object.keys(theme.cards).map(key => ( 134 | 135 | {key} 136 | 137 | ))} 138 | t.util.gx('cyan', 'blue'), 141 | color: 'white' 142 | }} 143 | > 144 | 145 | Gradient BG 146 | 147 | 148 | theme.util.gx('color1', 'color2') 149 | 150 | 151 | 152 | t.util.gxText('cyan', 'blue')} 156 | my={0} 157 | > 158 | Gradient text 159 | 160 | 161 | theme.util.gxText('color1', 'color2') 162 | 163 | 164 | 165 | Forms 166 | 167 | 171 | 184 | 188 | 189 | 192 | 195 | 198 | 199 |