├── .changeset ├── README.md └── config.json ├── .eslintrc.js ├── .gitignore ├── README.md ├── favicon.ico ├── frontity.settings.js ├── package-lock.json ├── package.json ├── packages └── frontity-chakra-theme │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── package.json │ └── src │ ├── assets │ ├── pattern-tile-green.svg │ └── pattern-tile-light-fade.svg │ ├── components │ ├── archive │ │ ├── archive-header.js │ │ ├── archive-item.js │ │ ├── archive.js │ │ ├── homepage-archive.js │ │ ├── index.js │ │ ├── pagination.js │ │ └── post-preview.js │ ├── featured-post │ │ ├── components.js │ │ ├── featured-post.js │ │ ├── genarate-gradient.js │ │ └── index.js │ ├── footer.js │ ├── header │ │ ├── header.js │ │ ├── index.js │ │ ├── navigation.js │ │ └── social-menu.js │ ├── helpers.js │ ├── hooks │ │ ├── useScrollProgress.js │ │ └── useSearch.js │ ├── index.js │ ├── link.js │ ├── loading.js │ ├── menu │ │ ├── index.js │ │ ├── menu-button.js │ │ ├── menu-drawer.js │ │ └── menu-item.js │ ├── nav.js │ ├── newsletter.js │ ├── page404.js │ ├── post │ │ ├── author-bio.js │ │ ├── featured-media.js │ │ ├── post-categories.js │ │ ├── post-header.js │ │ ├── post-progressbar.js │ │ └── post.js │ ├── search │ │ ├── index.js │ │ ├── search-button.js │ │ ├── search-form.js │ │ ├── search-modal.js │ │ └── search-results.js │ ├── styles │ │ ├── border-box.js │ │ ├── font-face.js │ │ ├── pattern-box.js │ │ ├── processors.js │ │ └── section.js │ └── title.js │ ├── fonts │ ├── KelsonSans-Bold.ttf │ └── KelsonSans-Bold.woff │ └── index.js ├── sandbox.config.json └── vercel.json /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/master/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@0.3.0/schema.json", 3 | "changelog": [ 4 | "@changesets/changelog-github", 5 | { "repo": "chakra-ui/frontity-chakra-ui-theme" } 6 | ], 7 | "commit": false, 8 | "linked": [], 9 | "access": "public", 10 | "baseBranch": "master" 11 | } 12 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true 5 | }, 6 | extends: ["eslint:recommended", "plugin:react/recommended"], 7 | globals: { 8 | Atomics: "readonly", 9 | SharedArrayBuffer: "readonly" 10 | }, 11 | parserOptions: { 12 | ecmaFeatures: { 13 | jsx: true 14 | }, 15 | ecmaVersion: 2018, 16 | sourceType: "module" 17 | }, 18 | plugins: ["react"], 19 | rules: { 20 | "react/prop-types": "off", 21 | "react/display-name": "off" 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log* 2 | yarn-debug.log* 3 | yarn-error.log* 4 | .DS_Store 5 | node_modules 6 | dist 7 | build 8 | analyze 9 | .env 10 | report.* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frontity Chakra Theme 2 | 3 | A Frontity theme made with the Chakra UI library. 4 | 5 | You can try it out in codesandbox: https://githubbox.com/chakra-ui/frontity-chakra-ui-theme 6 | 7 | ## Installation guide 8 | 9 | To get start quickly with the Chakra UI theme, you can install them like other published packages in Node using `npm` or `yarn`. 10 | 11 | To install, run this command in your terminal: 12 | 13 | ```sh 14 | npm install frontity-chakra-theme 15 | ``` 16 | 17 | Then add `frontity-chakra-theme` to your `frontity.settings.js` file. 18 | 19 | If you want to modify this package, you need to install it as a local package. You can do so by following this guide: [How to install a local package in Frontity](https://docs.frontity.org/guides/install-a-new-package#local-packages). 20 | 21 | ## Theme Options 22 | 23 | Chakra theme can be configures via the `frontity.settings.js` file. The theme options can be specified in the `state.theme` property. 24 | 25 | | Key | Default Value | Description | 26 | | ----------------------- | ------------- | -------------------------------------------- | 27 | | `menu` | [] | The top level navigation links for your blog | 28 | | `socialLinks` | [] | The social media links to use in your theme | 29 | | `logo` | [] | The text or logo image url | 30 | | `showBackgroundPattern` | `true` | If `true`, will show a backgroung pattern | 31 | | `showSocialLinks` | `true` | If `true`, will show the social media links | 32 | | `colors` | | The `primary` and `accent` colors to use | 33 | 34 | ## Example Usage 35 | 36 | ```js 37 | // frontity.settings.js 38 | const settings = { 39 | packages: [ 40 | { 41 | name: "frontity-chakra-theme", 42 | state: { 43 | theme: { 44 | // The logo can be a text or an image url 45 | logo: "Frontity", 46 | // show background pattern 47 | showBackgroundPattern: true, 48 | // show social links 49 | showSocialLinks: true, 50 | // the top-level navigation labels and links 51 | menu: [ 52 | ["Home", "/"], 53 | ["Portfolio", "/portfolio"], 54 | ["About", "/about"], 55 | ["Contact", "/contact"], 56 | ], 57 | // the social links 58 | socialLinks: [ 59 | ["pinterest", "https://www.pinterest.com/frontity/"], 60 | ["facebook", "https://www.instagram.com/frontity/"], 61 | ["twitter", "https://www.twitter.com/frontity/"], 62 | ], 63 | // color shades to use in the blog 64 | colors: { 65 | primary: { 66 | 50: "#e9f5f2", 67 | 100: "#d4dcd9", 68 | 200: "#bbc3be", 69 | 300: "#a1aba5", 70 | 400: "#87938b", 71 | 500: "#6d7972", 72 | 600: "#555f58", 73 | 700: "#323c34", 74 | 800: "#232924", 75 | 900: "#272727", 76 | }, 77 | accent: { 78 | 50: "#ede4d3", 79 | 100: "#fbe3b2", 80 | 200: "#f6d086", 81 | 300: "#f1be58", 82 | 400: "#eca419", 83 | 500: "#d49212", 84 | 600: "#a5710b", 85 | 700: "#775105", 86 | 800: "#483100", 87 | 900: "#1d0f00", 88 | }, 89 | }, 90 | }, 91 | }, 92 | }, 93 | ], 94 | }; 95 | 96 | export default settings; 97 | ``` 98 | 99 | **🚨NOTE**: Since this theme is based on Chakra, we require that the theme colors should be color values from `50` - `900`. For example, here's what the default colors look like: 100 | 101 | ```json 102 | // value of theme.colors 103 | { 104 | "primary": { 105 | "50": "#e9f5f2", 106 | "100": "#d4dcd9", 107 | "200": "#bbc3be", 108 | "300": "#a1aba5", 109 | "400": "#87938b", 110 | "500": "#6d7972", 111 | "600": "#555f58", 112 | "700": "#323c34", 113 | "800": "#232924", 114 | "900": "#272727" 115 | }, 116 | "accent": { 117 | "50": "#ede4d3", 118 | "100": "#fbe3b2", 119 | "200": "#f6d086", 120 | "300": "#f1be58", 121 | "400": "#eca419", 122 | "500": "#d49212", 123 | "600": "#a5710b", 124 | "700": "#775105", 125 | "800": "#483100", 126 | "900": "#1d0f00" 127 | } 128 | } 129 | ``` 130 | 131 | > You can use tools like Smart Swatch (https://smart-swatch.netlify.com/) or Palx (https://palx.jxnblk.com/) to generate color hues based on a single color 132 | 133 | ## Additional Settings 134 | 135 | In addition to the theme options, there are a handful of items you can customize via the `frontity` object in your site’s `frontity.settings.js` 136 | 137 | ```js 138 | // frontity.settings.js 139 | const settings = { 140 | state: { 141 | frontity: { 142 | url: "your website url", 143 | title: "Your website title", 144 | description: "Your website description", 145 | }, 146 | }, 147 | }; 148 | ``` 149 | 150 | If you ever have questions, kindly post issues here or head over to `https://community.frontity.org` to get more personalizes support. 151 | 152 | Enjoy the Chakra Theme ⚡️ 153 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chakra-ui/frontity-chakra-ui-theme/d78956a45874292c6c15e333cda098b4a273de40/favicon.ico -------------------------------------------------------------------------------- /frontity.settings.js: -------------------------------------------------------------------------------- 1 | const settings = { 2 | name: "chakra-react", 3 | state: { 4 | frontity: { 5 | url: "https://test.frontity.org", 6 | title: "Test Frontity Blog", 7 | description: "WordPress installation for Frontity development" 8 | } 9 | }, 10 | packages: [ 11 | { 12 | name: "frontity-chakra-theme", 13 | state: { 14 | theme: { 15 | menu: [ 16 | ["Home", "/"], 17 | ["Nature", "/category/nature/"], 18 | ["Travel", "/category/travel/"], 19 | ["Japan", "/tag/japan/"], 20 | ["About Us", "/about-us/"] 21 | ], 22 | socialLinks: [ 23 | ["pinterest", "https://www.pinterest.com/frontity/"], 24 | ["facebook", "https://www.instagram.com/frontity/"], 25 | ["twitter", "https://www.twitter.com/frontity/"] 26 | ], 27 | featured: { 28 | showOnArchive: true, 29 | showOnPost: true 30 | } 31 | } 32 | } 33 | }, 34 | { 35 | name: "@frontity/wp-source", 36 | state: { 37 | source: { 38 | url: "https://test.frontity.org" 39 | } 40 | } 41 | }, 42 | "@frontity/tiny-router", 43 | "@frontity/html2react" 44 | ] 45 | }; 46 | 47 | export default settings; 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-frontity-project", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "Frontity project", 6 | "keywords": [ 7 | "frontity" 8 | ], 9 | "scripts": { 10 | "dev": "frontity dev", 11 | "build": "frontity build", 12 | "serve": "frontity serve" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/chakra-ui/frontity-chakra-ui-theme.git" 17 | }, 18 | "author": "Segun Adebayo ", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/chakra-ui/frontity-chakra-ui-theme/issues" 22 | }, 23 | "homepage": "https://github.com/chakra-ui/frontity-chakra-ui-theme#readme", 24 | "prettier": {}, 25 | "workspaces": [ 26 | "packages/*" 27 | ], 28 | "dependencies": { 29 | "@frontity/core": "^1.11.0", 30 | "@frontity/html2react": "^1.6.1", 31 | "@frontity/tiny-router": "^1.4.0", 32 | "@frontity/wp-source": "^1.11.2", 33 | "frontity": "^1.14.2", 34 | "frontity-chakra-theme": "./packages/frontity-chakra-theme" 35 | }, 36 | "devDependencies": { 37 | "@changesets/changelog-github": "^0.2.8", 38 | "@changesets/cli": "^2.14.1", 39 | "eslint": "^7.21.0", 40 | "eslint-plugin-react": "^7.22.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # frontity-chakra-theme 2 | 3 | ## 1.0.3 4 | 5 | ### Patch Changes 6 | 7 | - [`d0b6381`](https://github.com/chakra-ui/frontity-chakra-ui-theme/commit/d0b6381a8553da27dfa60b85eae0fbd46df8026d) [#32](https://github.com/chakra-ui/frontity-chakra-ui-theme/pull/32) Thanks [@luisherranz](https://github.com/luisherranz)! - Fix search hook bug. 8 | 9 | ## 1.0.2 10 | 11 | ### Patch Changes 12 | 13 | - [`b935a06`](https://github.com/chakra-ui/frontity-chakra-ui-theme/commit/b935a06d571a24214f325cd6b27ffc4324b2eaf7) [#29](https://github.com/chakra-ui/frontity-chakra-ui-theme/pull/29) Thanks [@luisherranz](https://github.com/luisherranz)! - Simplify search using new data properties. 14 | 15 | ## 1.0.1 16 | 17 | ### Patch Changes 18 | 19 | - [`5759ce8`](https://github.com/chakra-ui/frontity-chakra-ui-theme/commit/5759ce80f95b262f416e5a1d6ab094dbe83a15fd) [#28](https://github.com/chakra-ui/frontity-chakra-ui-theme/pull/28) Thanks [@luisherranz](https://github.com/luisherranz)! - Add Reamde file. 20 | 21 | ## 1.0.0 22 | 23 | ### Major Changes 24 | 25 | - 2bd9c4d: Update Chakra to v1 and Frontity to its latest version. 26 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2018 Worona Labs SL 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/README.md: -------------------------------------------------------------------------------- 1 | # Frontity Chakra Theme 2 | 3 | A Frontity theme made with the Chakra UI library. 4 | 5 | You can try it out in codesandbox: https://githubbox.com/chakra-ui/frontity-chakra-ui-theme 6 | 7 | ## Installation guide 8 | 9 | To get start quickly with the Chakra UI theme, you can install them like other published packages in Node using `npm` or `yarn`. 10 | 11 | To install, run this command in your terminal: 12 | 13 | ```sh 14 | npm install frontity-chakra-theme 15 | ``` 16 | 17 | Then add `frontity-chakra-theme` to your `frontity.settings.js` file. 18 | 19 | If you want to modify this package, you need to install it as a local package. You can do so by following this guide: [How to install a local package in Frontity](https://docs.frontity.org/guides/install-a-new-package#local-packages). 20 | 21 | ## Theme Options 22 | 23 | Chakra theme can be configures via the `frontity.settings.js` file. The theme options can be specified in the `state.theme` property. 24 | 25 | | Key | Default Value | Description | 26 | | ----------------------- | ------------- | -------------------------------------------- | 27 | | `menu` | [] | The top level navigation links for your blog | 28 | | `socialLinks` | [] | The social media links to use in your theme | 29 | | `logo` | [] | The text or logo image url | 30 | | `showBackgroundPattern` | `true` | If `true`, will show a backgroung pattern | 31 | | `showSocialLinks` | `true` | If `true`, will show the social media links | 32 | | `colors` | | The `primary` and `accent` colors to use | 33 | 34 | ## Example Usage 35 | 36 | ```js 37 | // frontity.settings.js 38 | const settings = { 39 | packages: [ 40 | { 41 | name: "frontity-chakra-theme", 42 | state: { 43 | theme: { 44 | // The logo can be a text or an image url 45 | logo: "Frontity", 46 | // show background pattern 47 | showBackgroundPattern: true, 48 | // show social links 49 | showSocialLinks: true, 50 | // the top-level navigation labels and links 51 | menu: [ 52 | ["Home", "/"], 53 | ["Portfolio", "/portfolio"], 54 | ["About", "/about"], 55 | ["Contact", "/contact"], 56 | ], 57 | // the social links 58 | socialLinks: [ 59 | ["pinterest", "https://www.pinterest.com/frontity/"], 60 | ["facebook", "https://www.instagram.com/frontity/"], 61 | ["twitter", "https://www.twitter.com/frontity/"], 62 | ], 63 | // color shades to use in the blog 64 | colors: { 65 | primary: { 66 | 50: "#e9f5f2", 67 | 100: "#d4dcd9", 68 | 200: "#bbc3be", 69 | 300: "#a1aba5", 70 | 400: "#87938b", 71 | 500: "#6d7972", 72 | 600: "#555f58", 73 | 700: "#323c34", 74 | 800: "#232924", 75 | 900: "#272727", 76 | }, 77 | accent: { 78 | 50: "#ede4d3", 79 | 100: "#fbe3b2", 80 | 200: "#f6d086", 81 | 300: "#f1be58", 82 | 400: "#eca419", 83 | 500: "#d49212", 84 | 600: "#a5710b", 85 | 700: "#775105", 86 | 800: "#483100", 87 | 900: "#1d0f00", 88 | }, 89 | }, 90 | }, 91 | }, 92 | }, 93 | ], 94 | }; 95 | 96 | export default settings; 97 | ``` 98 | 99 | **🚨NOTE**: Since this theme is based on Chakra, we require that the theme colors should be color values from `50` - `900`. For example, here's what the default colors look like: 100 | 101 | ```json 102 | // value of theme.colors 103 | { 104 | "primary": { 105 | "50": "#e9f5f2", 106 | "100": "#d4dcd9", 107 | "200": "#bbc3be", 108 | "300": "#a1aba5", 109 | "400": "#87938b", 110 | "500": "#6d7972", 111 | "600": "#555f58", 112 | "700": "#323c34", 113 | "800": "#232924", 114 | "900": "#272727" 115 | }, 116 | "accent": { 117 | "50": "#ede4d3", 118 | "100": "#fbe3b2", 119 | "200": "#f6d086", 120 | "300": "#f1be58", 121 | "400": "#eca419", 122 | "500": "#d49212", 123 | "600": "#a5710b", 124 | "700": "#775105", 125 | "800": "#483100", 126 | "900": "#1d0f00" 127 | } 128 | } 129 | ``` 130 | 131 | > You can use tools like Smart Swatch (https://smart-swatch.netlify.com/) or Palx (https://palx.jxnblk.com/) to generate color hues based on a single color 132 | 133 | ## Additional Settings 134 | 135 | In addition to the theme options, there are a handful of items you can customize via the `frontity` object in your site’s `frontity.settings.js` 136 | 137 | ```js 138 | // frontity.settings.js 139 | const settings = { 140 | state: { 141 | frontity: { 142 | url: "your website url", 143 | title: "Your website title", 144 | description: "Your website description", 145 | }, 146 | }, 147 | }; 148 | ``` 149 | 150 | If you ever have questions, kindly post issues here or head over to `https://community.frontity.org` to get more personalizes support. 151 | 152 | Enjoy the Chakra Theme ⚡️ 153 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontity-chakra-theme", 3 | "version": "1.0.3", 4 | "description": "A frontity theme made with Chakra UI", 5 | "keywords": [ 6 | "wordpress", 7 | "frontity", 8 | "frontity-theme", 9 | "frontity-amp" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/chakra-ui/frontity-chakra-ui-theme.git" 14 | }, 15 | "author": "Segun Adebayo ", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/chakra-ui/frontity-chakra-ui-theme/issues" 19 | }, 20 | "homepage": "https://github.com/chakra-ui/frontity-chakra-ui-theme#readme", 21 | "publishConfig": { 22 | "access": "public" 23 | }, 24 | "dependencies": { 25 | "@chakra-ui/react": "^1.3.3", 26 | "frontity": "^1.14.2", 27 | "react-icons": "^4.2.0", 28 | "framer-motion": "^3.9.2", 29 | "@frontity/components": "^1.7.1", 30 | "@frontity/hooks": "^2.2.0", 31 | "@frontity/html2react": "^1.6.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/assets/pattern-tile-green.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/archive/archive-header.js: -------------------------------------------------------------------------------- 1 | import { Heading, Text } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import { PatternBox, PatternBoxInner } from "../styles/pattern-box"; 4 | 5 | const ArchiveHeader = ({ taxonomy, title, ...props }) => ( 6 | 7 | 8 | 9 | {taxonomy} 10 | 11 | 17 | {title} 18 | 19 | 20 | 21 | ); 22 | 23 | export default ArchiveHeader; 24 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/archive/archive-item.js: -------------------------------------------------------------------------------- 1 | import { connect } from "frontity"; 2 | import React from "react"; 3 | import { formatPostData } from "../helpers"; 4 | import PostPreview from "./post-preview"; 5 | 6 | const ArchiveItem = ({ state, item }) => { 7 | const data = formatPostData(state, item); 8 | return ; 9 | }; 10 | 11 | export default connect(ArchiveItem); 12 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/archive/archive.js: -------------------------------------------------------------------------------- 1 | import { Box, SimpleGrid } from "@chakra-ui/react"; 2 | import { connect } from "frontity"; 3 | import React from "react"; 4 | import ArchiveHeader from "./archive-header"; 5 | import ArchiveItem from "./archive-item"; 6 | import HomepageArchive from "./homepage-archive"; 7 | import Pagination from "./pagination"; 8 | import { decode } from "frontity"; 9 | 10 | const Archive = ({ state }) => { 11 | // Get the data of the current list. 12 | const data = state.source.get(state.router.link); 13 | 14 | if (data.isHome) return ; 15 | 16 | return ( 17 | 18 | {/* If the list is a taxonomy, we render a title. */} 19 | {data.isTaxonomy && ( 20 | 25 | )} 26 | 27 | {/* If the list is an author, we render a title. */} 28 | {data.isAuthor && ( 29 | 34 | )} 35 | 36 | 43 | {/* Iterate over the items of the list. */} 44 | 45 | {data.items.map(({ type, id }) => { 46 | const item = state.source[type][id]; 47 | return ; 48 | })} 49 | 50 | 51 | 52 | 53 | 54 | ); 55 | }; 56 | 57 | export default connect(Archive); 58 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/archive/homepage-archive.js: -------------------------------------------------------------------------------- 1 | import { Box, Heading, SimpleGrid } from "@chakra-ui/react"; 2 | import { connect } from "frontity"; 3 | import React from "react"; 4 | import { FeaturedPostSection } from "../featured-post/featured-post"; 5 | import { formatPostData, splitPosts } from "../helpers"; 6 | import { Newsletter } from "../newsletter"; 7 | import ArchiveItem from "./archive-item"; 8 | import { PaginationButton } from "./pagination"; 9 | 10 | const HomepageArchive = ({ state, libraries }) => { 11 | // Get the data of the current list. 12 | const data = state.source.get(state.router.link); 13 | 14 | const [firstThreePosts, othersPosts] = splitPosts(state, data.items); 15 | 16 | return ( 17 | 18 | formatPostData(state, post))} 20 | /> 21 | 28 | 34 | Latest Posts 35 | 36 | 37 | 42 | {othersPosts.map(({ type, id }) => { 43 | const item = state.source[type][id]; 44 | return ; 45 | })} 46 | 47 | 48 | 49 | More posts 50 | 51 | 52 | {libraries.newsletter && ( 53 | 54 | )} 55 | 56 | ); 57 | }; 58 | 59 | export default connect(HomepageArchive); 60 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/archive/index.js: -------------------------------------------------------------------------------- 1 | import { loadable } from "frontity"; 2 | 3 | // Codesplit the list component so it's not included if the users 4 | // load a post directly. 5 | export default loadable(() => import("./archive")); 6 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/archive/pagination.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { connect, styled } from "frontity"; 3 | import Link from "../link"; 4 | import { IoIosArrowRoundForward, IoIosArrowRoundBack } from "react-icons/io"; 5 | import { Box, Stack } from "@chakra-ui/react"; 6 | 7 | export const PaginationButton = styled(Link)` 8 | width: 100%; 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | font-size: 1.2rem; 13 | padding: 0.8rem 1rem; 14 | min-height: 60px; 15 | 16 | cursor: pointer; 17 | border: none; 18 | background: #333a35; 19 | color: #eca419; 20 | 21 | &:hover { 22 | background-color: #48584d; 23 | color: #eca419; 24 | } 25 | 26 | &[aria-disabled="true"] { 27 | background-color: #dfd7c7; 28 | cursor: auto; 29 | color: #a0a0a0; 30 | } 31 | `; 32 | 33 | export const PrevLink = ({ 34 | isDisabled, 35 | label = "See older posts", 36 | link, 37 | ...props 38 | }) => ( 39 | 40 | 41 | 42 | Older posts 43 | 44 | 45 | ); 46 | 47 | export const NextLink = ({ 48 | isDisabled, 49 | label = "See newer posts", 50 | link, 51 | ...props 52 | }) => ( 53 | 54 | 55 | Newer posts 56 | 57 | 58 | 59 | ); 60 | 61 | const Pagination = ({ state, actions, libraries, ...props }) => { 62 | const { totalPages } = state.source.get(state.router.link); 63 | const { path, page, query } = libraries.source.parse(state.router.link); 64 | 65 | const isThereNextPage = page > 1; 66 | const isTherePreviousPage = page < totalPages; 67 | 68 | const nextPageLink = libraries.source.stringify({ 69 | path, 70 | page: page + 1, 71 | query 72 | }); 73 | 74 | const prevPageLink = libraries.source.stringify({ 75 | path, 76 | page: page - 1, 77 | query 78 | }); 79 | 80 | // Fetch the next page if it hasn't been fetched yet. 81 | useEffect(() => { 82 | if (isThereNextPage) actions.source.fetch(nextPageLink); 83 | }, []); 84 | 85 | return ( 86 | 87 | 88 | 89 | 90 | ); 91 | }; 92 | 93 | export default connect(Pagination); 94 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/archive/post-preview.js: -------------------------------------------------------------------------------- 1 | import { Box, Flex, Heading } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import { PostImageWithOverlay } from "../featured-post/components"; 4 | import Link from "../link"; 5 | import PostCategories from "../post/post-categories"; 6 | 7 | const PostPreview = ({ data, ...rest }) => { 8 | const { title, excerpt, featured_media, link, categories } = data; 9 | 10 | return ( 11 | 19 | {/* Use the frontity settings for featuredPost here */} 20 | {featured_media && featured_media.src && ( 21 | 22 | 23 | 24 | )} 25 | 26 | 27 | 28 | {title} 29 | 30 | 36 | 41 | 42 | 43 | ); 44 | }; 45 | 46 | export default PostPreview; 47 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/featured-post/components.js: -------------------------------------------------------------------------------- 1 | import { Box, Heading } from "@chakra-ui/react"; 2 | import { styled } from "frontity"; 3 | import React from "react"; 4 | import Link from "../link"; 5 | import Image from "@frontity/components/image"; 6 | 7 | export const PostLink = styled(Link)` 8 | width: 100%; 9 | height: 100%; 10 | position: absolute; 11 | top: 0; 12 | left: 0; 13 | z-index: 1; 14 | `; 15 | 16 | export const PostContent = props => ( 17 | 29 | ); 30 | 31 | export const PostTitle = props => ( 32 | 40 | ); 41 | 42 | export const PostOverlay = props => ( 43 | 57 | ); 58 | 59 | export const PostImageWithOverlay = ({ src, alt, srcSet, ...props }) => ( 60 | 68 | 69 | 70 | 71 | ); 72 | 73 | export const PrimaryPostArticle = props => ( 74 | 86 | ); 87 | 88 | export const SecondaryPostArticle = props => ( 89 | 100 | ); 101 | 102 | export const PostImage = props => ( 103 | 115 | ); 116 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/featured-post/featured-post.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | PostContent, 4 | PostImage, 5 | PostOverlay, 6 | PostTitle, 7 | PrimaryPostArticle, 8 | SecondaryPostArticle 9 | } from "./components"; 10 | import generateGradient from "./genarate-gradient"; 11 | import { Flex, Box } from "@chakra-ui/react"; 12 | import PostCategories from "../post/post-categories"; 13 | import Link from "../link"; 14 | 15 | export const PrimaryPostPreview = ({ data, ...props }) => { 16 | const { title, categories, featured_media, link } = data; 17 | 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | {title} 25 | 26 | 27 | 28 | 29 | ); 30 | }; 31 | 32 | export const SecondaryPostPreview = ({ data, ...props }) => { 33 | const { title, categories, link, featured_media } = data; 34 | 35 | return ( 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {title} 44 | 45 | 46 | 47 | 48 | ); 49 | }; 50 | 51 | export const FeaturedPostSection = ({ data, ...props }) => ( 52 | 53 | 54 | 55 | 56 | 61 | 62 | 63 | 64 | 65 | ); 66 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/featured-post/genarate-gradient.js: -------------------------------------------------------------------------------- 1 | function generateGradient() { 2 | var hexValues = [ 3 | "0", 4 | "1", 5 | "2", 6 | "3", 7 | "4", 8 | "5", 9 | "6", 10 | "7", 11 | "8", 12 | "9", 13 | "a", 14 | "b", 15 | "c", 16 | "d", 17 | "e" 18 | ]; 19 | 20 | function populate(a) { 21 | for (var i = 0; i < 6; i++) { 22 | var x = Math.round(Math.random() * 14); 23 | var y = hexValues[x]; 24 | a += y; 25 | } 26 | return a; 27 | } 28 | 29 | var newColor1 = populate("#"); 30 | var newColor2 = populate("#"); 31 | var angle = Math.round(Math.random() * 240); 32 | 33 | var gradient = 34 | "linear-gradient(" + angle + "deg, " + newColor1 + ", " + newColor2 + ")"; 35 | 36 | return gradient; 37 | } 38 | 39 | export default generateGradient; 40 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/featured-post/index.js: -------------------------------------------------------------------------------- 1 | export * from "./featured-post"; 2 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/footer.js: -------------------------------------------------------------------------------- 1 | import { Box, SimpleGrid } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import { SocialMenu } from "./header/social-menu"; 4 | import { connect } from "frontity"; 5 | 6 | const FooterSection = props => ( 7 | 14 | ); 15 | 16 | const FooterSectionGroup = props => ( 17 | 24 | ); 25 | 26 | const FooterSectionItem = props => ( 27 | 28 | ); 29 | 30 | const Footer = ({ state }) => ( 31 | 32 | 33 | 38 | © {new Date().getFullYear()} Frontity 39 | 40 | 41 | 42 | 47 | 48 | 49 | 54 | Made with Frontity 55 | 56 | 57 | 58 | ); 59 | 60 | export default connect(Footer); 61 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/header/header.js: -------------------------------------------------------------------------------- 1 | import { Box, Flex } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import Link from "../link"; 4 | import MobileMenu from "../menu"; 5 | import { isUrl, omitConnectProps } from "../helpers"; 6 | import { connect } from "frontity"; 7 | 8 | const SiteHeader = props => ( 9 | 20 | ); 21 | 22 | const SiteHeaderInner = props => ( 23 | 31 | ); 32 | 33 | const Logo = ({ isImage = true, src }) => 34 | isImage ? ( 35 | 36 | ) : ( 37 | 44 | {src} 45 | 46 | ); 47 | 48 | const SiteLogo = connect(({ state, ...props }) => { 49 | // check if the logo is a url, 50 | // we assume, if it's a url, it points to an image, else it's a text 51 | const isImage = isUrl(state.theme.logo); 52 | return ( 53 | 54 | 55 | 56 | 57 | 58 | ); 59 | }); 60 | 61 | const Header = ({ children, ...props }) => ( 62 | 63 | 64 | 65 | 66 | {children} 67 | 68 | 69 | ); 70 | 71 | export default Header; 72 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/header/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from "frontity"; 2 | import React from "react"; 3 | import MainHeader from "./header"; 4 | import Navigation from "./navigation"; 5 | import SocialNav from "./social-menu"; 6 | import { SearchButton, SearchModal, SearchForm } from "../search"; 7 | 8 | const Header = ({ state, actions }) => ( 9 | 10 | 11 | {state.theme.showSocialLinks && ( 12 | 13 | )} 14 | 15 | 19 | 20 | 21 | 22 | ); 23 | 24 | export default connect(Header); 25 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/header/navigation.js: -------------------------------------------------------------------------------- 1 | import { Box, Stack } from "@chakra-ui/react"; 2 | import { styled } from "frontity"; 3 | import React from "react"; 4 | import FrontityLink from "../link"; 5 | 6 | const Link = styled(FrontityLink)` 7 | position: relative; 8 | color: #fff; 9 | text-decoration: none; 10 | 11 | &:after { 12 | transition: bottom ease 0.25s, background-color ease 0.25s; 13 | content: ""; 14 | width: 100%; 15 | height: 2px; 16 | position: absolute; 17 | bottom: 0; 18 | left: 0; 19 | background: transparent; 20 | } 21 | 22 | &:hover { 23 | &:after { 24 | bottom: -5px; 25 | background-color: ${p => p.theme.colors.accent[400]}; 26 | } 27 | } 28 | `; 29 | 30 | export const SiteMenu = props => ( 31 | 41 | ); 42 | 43 | const SiteMenuItem = ({ link, ...props }) => ( 44 | 55 | {props.children} 56 | 57 | ); 58 | 59 | const Navigation = ({ menu, ...props }) => ( 60 | 61 | 62 | {menu.map(([name, link]) => ( 63 | 64 | {name} 65 | 66 | ))} 67 | 68 | 69 | ); 70 | 71 | export default Navigation; 72 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/header/social-menu.js: -------------------------------------------------------------------------------- 1 | import { Box, VisuallyHidden } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import { SiteMenu } from "./navigation"; 4 | import { 5 | IoLogoTwitter, 6 | IoLogoPinterest, 7 | IoLogoFacebook, 8 | IoLogoInstagram 9 | } from "react-icons/io"; 10 | import Link from "../link"; 11 | 12 | // warning for showSocialLinks and menu.length 13 | export const SocialMenu = ({ menu, ...props }) => ( 14 | 15 | {menu.map(([name, link]) => { 16 | const SocialIcon = icons[name]; 17 | return ( 18 | 19 | ); 20 | })} 21 | 22 | ); 23 | 24 | const SocialMenuItem = ({ icon, label, link, ...props }) => ( 25 | 34 | 35 | 36 | 37 | {label} 38 | 39 | ); 40 | 41 | const icons = { 42 | twitter: IoLogoTwitter, 43 | pinterest: IoLogoPinterest, 44 | facebook: IoLogoFacebook, 45 | instagram: IoLogoInstagram 46 | }; 47 | 48 | const SocialNav = ({ menu, ...props }) => ( 49 | 50 | 51 | 52 | ); 53 | 54 | export default SocialNav; 55 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/helpers.js: -------------------------------------------------------------------------------- 1 | function getSrcSet(media) { 2 | const srcset = 3 | Object.values(media.media_details.sizes) 4 | // Get the url and width of each size. 5 | .map(item => [item.source_url, item.width]) 6 | // Recude them to a string with the format required by `srcset`. 7 | .reduce( 8 | (final, current, index, array) => 9 | final.concat( 10 | `${current.join(" ")}w${index !== array.length - 1 ? ", " : ""}` 11 | ), 12 | "" 13 | ) || null; 14 | return srcset; 15 | } 16 | 17 | export function getMediaAttributes(state, id) { 18 | const media = state.source.attachment[id]; 19 | if (!media) return {}; 20 | 21 | const srcSet = getSrcSet(media); 22 | 23 | return { 24 | id, 25 | alt: media.alt_text, 26 | src: media.source_url, 27 | srcSet 28 | }; 29 | } 30 | 31 | export function getPostCategories(state, post) { 32 | const allCategories = state.source.category; 33 | const categories = 34 | post.categories && post.categories.map(catId => allCategories[catId]); 35 | return categories ? categories.filter(Boolean) : []; 36 | } 37 | 38 | export function getPostAuthor(state, post) { 39 | return state.source.author[post.author]; 40 | } 41 | 42 | export function getPostTags(state, post) { 43 | const allTags = state.source.tag; 44 | const tags = post.tags && post.tags.map(tagId => allTags[tagId]); 45 | return tags ? tags.filter(Boolean) : []; 46 | } 47 | 48 | export function getPostData(state) { 49 | const data = state.source.get(state.router.link); 50 | const post = state.source[data.type][data.id]; 51 | return { ...post, isReady: data.isReady, isPage: data.isPage }; 52 | } 53 | 54 | export function formatPostData(state, post) { 55 | return { 56 | id: post.id, 57 | author: getPostAuthor(state, post), 58 | publishDate: post.date, 59 | title: post.title.rendered, 60 | categories: getPostCategories(state, post), 61 | tags: getPostTags(state, post), 62 | link: post.link, 63 | featured_media: getMediaAttributes(state, post.featured_media), 64 | content: post.content.rendered, 65 | excerpt: post.excerpt.rendered 66 | }; 67 | } 68 | 69 | export function splitPosts(state, routeData) { 70 | const firstThreePosts = []; 71 | const otherPosts = []; 72 | 73 | routeData.forEach((item, idx) => { 74 | const itemData = state.source[item.type][item.id]; 75 | if (idx < 3) firstThreePosts.push(itemData); 76 | else otherPosts.push(itemData); 77 | }); 78 | 79 | return [firstThreePosts, otherPosts]; 80 | } 81 | 82 | export function omitConnectProps(props) { 83 | const out = {}; 84 | const propsToOmit = [ 85 | "state", 86 | "actions", 87 | "roots", 88 | "fills", 89 | "libraries", 90 | "getSnapshot" 91 | ]; 92 | const isGetSnapshot = prop => 93 | typeof prop === "function" && prop.name === "getSnapshot"; 94 | 95 | for (const prop in props) { 96 | if (propsToOmit.includes(prop) || isGetSnapshot(prop)) continue; 97 | out[prop] = props[prop]; 98 | } 99 | 100 | return out; 101 | } 102 | 103 | const monthNames = [ 104 | "January", 105 | "February", 106 | "March", 107 | "April", 108 | "May", 109 | "June", 110 | "July", 111 | "August", 112 | "September", 113 | "October", 114 | "Novemeber", 115 | "December" 116 | ]; 117 | 118 | const formatDay = day => { 119 | const lastLetter = day[day.length - 1]; 120 | if (lastLetter) return `${day}nd`; 121 | if (lastLetter) return `${day}st`; 122 | if (lastLetter) return `${day}rd`; 123 | return `${day}th`; 124 | }; 125 | 126 | export function formatDate(date) { 127 | const jsDate = new Date(date); 128 | const day = jsDate.getDate(); 129 | const month = jsDate.getMonth() + 1; 130 | const year = jsDate.getFullYear(); 131 | 132 | return `${formatDay(day)} ${monthNames[month]}, ${year}`; 133 | } 134 | 135 | export function isUrl(str) { 136 | var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!-/]))?/; 137 | return regexp.test(str); 138 | } 139 | 140 | export function debounce(fn) { 141 | let queued = null; 142 | return [ 143 | (...args) => { 144 | if (queued) cancelAnimationFrame(queued); 145 | queued = requestAnimationFrame(fn.bind(fn, ...args)); 146 | }, 147 | () => { 148 | cancelAnimationFrame(queued); 149 | } 150 | ]; 151 | } 152 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/hooks/useScrollProgress.js: -------------------------------------------------------------------------------- 1 | import { 2 | useRef, 3 | useLayoutEffect, 4 | useEffect, 5 | useState, 6 | useCallback 7 | } from "react"; 8 | import { debounce } from "../helpers"; 9 | 10 | // check if we're in a browser or server environment 11 | const isBrowser = typeof window !== `undefined`; 12 | 13 | // use the correct effect 14 | const useEnhancedEffect = isBrowser ? useLayoutEffect : useEffect; 15 | 16 | // get window scroll position 17 | function getScrollPosition() { 18 | if (!isBrowser) return { x: 0, y: 0 }; 19 | return { x: window.scrollX, y: window.scrollY }; 20 | } 21 | 22 | function useScrollEffect(effect) { 23 | // useCallback to keep the function reference the same 24 | // as long as `effect` stays the same 25 | const update = useCallback(() => { 26 | // get new scroll position 27 | const position = getScrollPosition(); 28 | 29 | // run effect with current position 30 | effect && effect(position); 31 | }, [effect]); 32 | 33 | useEnhancedEffect(() => { 34 | // run effect on mount 35 | update(); 36 | 37 | // performance: debounce update function calls 38 | const [debouncedUpdate, cancelUpdate] = debounce(update); 39 | 40 | // attach scroll listener 41 | window.addEventListener("scroll", debouncedUpdate); 42 | 43 | return () => { 44 | // perf: remove the event listener 45 | window.removeEventListener("scroll", debouncedUpdate); 46 | cancelUpdate(); 47 | }; 48 | }, []); 49 | } 50 | 51 | function useScrollProgress() { 52 | // create ref to track node's scrollHeight 53 | const ref = useRef(null); 54 | 55 | // state to store the scroll progress 56 | const [progress, setProgress] = useState(0); 57 | 58 | // let's use the sweet effect :) 59 | useScrollEffect(scrollPos => { 60 | // if there's no node, don't do anything 'coz there's no point 61 | if (!ref.current) return; 62 | 63 | // else, calculate the scroll percent 64 | const percent = (scrollPos.y / ref.current.scrollHeight) * 100; 65 | 66 | // update the state 67 | setProgress(percent); 68 | }); 69 | 70 | // return the goods :) 71 | return [ref, progress]; 72 | } 73 | 74 | export default useScrollProgress; 75 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/hooks/useSearch.js: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | 3 | const useSearch = ({ state, actions }) => { 4 | const data = state.source.get(state.router.link); 5 | 6 | // Keep a reference to the input so we can grab it's value on form submission 7 | const inputRef = useRef(); 8 | 9 | const onSubmit = event => { 10 | // Prevent page navigation 11 | event.preventDefault(); 12 | 13 | // Get the input's value 14 | const searchString = inputRef.current.value; 15 | 16 | // If the typed search string is not empty 17 | // Better to trim write spaces as well 18 | if (searchString.trim().length > 0) { 19 | // Let's go search for blogs that match the search string 20 | actions.router.set( 21 | `${state.source.postsPage}?s=${searchString.toLowerCase()}` 22 | ); 23 | 24 | // Scroll the page to the top 25 | window.scrollTo(0, 0); 26 | 27 | // Close the search modal 28 | actions.theme.closeSearchModal(); 29 | } 30 | }; 31 | 32 | return { 33 | form: { 34 | role: "search", 35 | "aria-label": "Search this blog", 36 | onSubmit 37 | }, 38 | input: { 39 | defaultValue: data.searchQuery && data.searchQuery.replace(/\+/g, " "), 40 | ref: inputRef 41 | } 42 | }; 43 | }; 44 | 45 | export default useSearch; 46 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/index.js: -------------------------------------------------------------------------------- 1 | import { Box, ChakraProvider, extendTheme } from "@chakra-ui/react"; 2 | import { connect, Head } from "frontity"; 3 | import Switch from "@frontity/components/switch"; 4 | import React from "react"; 5 | import Archive from "./archive"; 6 | import Footer from "./footer"; 7 | import Header from "./header"; 8 | import Loading from "./loading"; 9 | import Page404 from "./page404"; 10 | import Post from "./post/post"; 11 | import SearchResults from "./search"; 12 | import Title from "./title"; 13 | import FontFace from "./styles/font-face"; 14 | 15 | // Theme is the root React component of our theme. The one we will export 16 | // in roots. 17 | const Theme = ({ state }) => { 18 | // Get information about the current URL. 19 | const data = state.source.get(state.router.link); 20 | 21 | const overrides = extendTheme({ 22 | fonts: { 23 | heading: "Kelson, system-ui, Helvetica, sans-serif" 24 | }, 25 | colors: { ...state.theme.colors } 26 | }); 27 | 28 | return ( 29 | 30 | 31 | {/* Add some metatags to the of the HTML. */} 32 | 33 | <Head> 34 | <meta name="description" content={state.frontity.description} /> 35 | <html lang="en" /> 36 | </Head> 37 | 38 | {/* Add the header of the site. */} 39 | <Header /> 40 | 41 | {/* Add the main section. It renders a different component depending 42 | on the type of URL we are in. */} 43 | <Box 44 | as="main" 45 | mt={{ base: "40px", md: "70px" }} 46 | minH="calc(100vh - 320px)" 47 | > 48 | <Switch> 49 | <Loading when={data.isFetching} /> 50 | <SearchResults when={data.isSearch} /> 51 | <Archive when={data.isArchive} /> 52 | <Post when={data.isPostType} /> 53 | <Page404 when={data.is404} /> 54 | </Switch> 55 | </Box> 56 | 57 | <Footer /> 58 | </ChakraProvider> 59 | ); 60 | }; 61 | 62 | export default connect(Theme); 63 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/link.js: -------------------------------------------------------------------------------- 1 | import { Box } from "@chakra-ui/react"; 2 | import { connect } from "frontity"; 3 | import React from "react"; 4 | import { omitConnectProps } from "./helpers"; 5 | 6 | const Link = ({ 7 | state, 8 | actions, 9 | link, 10 | className, 11 | children, 12 | rel, 13 | "aria-current": ariaCurrent, 14 | ...props 15 | }) => { 16 | const isDisabled = props["aria-disabled"]; 17 | // If we're not in a frontity environment, let's just render the children 18 | if (state == null) 19 | return ( 20 | <a className={className} href={isDisabled ? undefined : "#"} {...props}> 21 | {children} 22 | </a> 23 | ); 24 | 25 | // Check if the link is an external or internal link 26 | const isExternal = link && link.startsWith("http"); 27 | 28 | const onClick = event => { 29 | // Do nothing if it's an external link 30 | if (isExternal || isDisabled) return; 31 | 32 | event.preventDefault(); 33 | event.stopPropagation(); 34 | 35 | // Set the router to the new url. 36 | actions.router.set(link); 37 | 38 | // Scroll the page to the top 39 | window.scrollTo(0, 0); 40 | 41 | // if the menu modal is open, close it so it doesn't block rendering 42 | if (state.theme.isMobileMenuOpen) { 43 | actions.theme.closeMobileMenu(); 44 | } 45 | 46 | if (props.onClick) { 47 | props.onClick(event); 48 | } 49 | }; 50 | 51 | return ( 52 | <Box 53 | as="a" 54 | href={isDisabled ? undefined : link} 55 | onClick={onClick} 56 | className={className} 57 | aria-current={ariaCurrent} 58 | rel={isExternal ? "noopener noreferrer" : rel} 59 | target={isExternal ? "_blank" : undefined} 60 | onMouseEnter={event => { 61 | // Prefetch the link's content when the user hovers on the link 62 | if (!isExternal) actions.source.fetch(link); 63 | if (props.onMouseEnter) props.onMouseEnter(event); 64 | }} 65 | {...omitConnectProps(props)} 66 | > 67 | {children} 68 | </Box> 69 | ); 70 | }; 71 | 72 | export default connect(Link); 73 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/loading.js: -------------------------------------------------------------------------------- 1 | import { Box, CircularProgress } from "@chakra-ui/react"; 2 | 3 | const Loading = () => ( 4 | <Box 5 | mx="auto" 6 | p="24px" 7 | display="flex" 8 | alignItems="center" 9 | justifyContent="center" 10 | minH="inherit" 11 | > 12 | <CircularProgress 13 | css={{ 14 | "[data-progress-indicator]": { 15 | color: "#eca419" 16 | } 17 | }} 18 | isIndeterminate 19 | /> 20 | </Box> 21 | ); 22 | 23 | export default Loading; 24 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/menu/index.js: -------------------------------------------------------------------------------- 1 | import { Stack, Box } from "@chakra-ui/react"; 2 | import { connect } from "frontity"; 3 | import React, { useRef } from "react"; 4 | import MenuButton from "./menu-button"; 5 | import MenuDrawer from "./menu-drawer"; 6 | import MenuItem from "./menu-item"; 7 | import { SocialMenu } from "../header/social-menu"; 8 | 9 | const Menu = ({ state, actions }) => { 10 | const { isMobileMenuOpen } = state.theme; 11 | const { openMobileMenu, closeMobileMenu } = actions.theme; 12 | const buttonRef = useRef(); 13 | return ( 14 | <> 15 | <MenuButton ref={buttonRef} onClick={openMobileMenu} /> 16 | 17 | <MenuDrawer 18 | finalFocusRef={buttonRef} 19 | isOpen={isMobileMenuOpen} 20 | onClose={closeMobileMenu} 21 | > 22 | <Stack mt="20%" spacing={6} direction="column" as="ul" ml="0"> 23 | {state.theme.menu.map(([name, link], index) => ( 24 | <MenuItem link={link} key={name} index={`0${index + 1}`}> 25 | {name} 26 | </MenuItem> 27 | ))} 28 | </Stack> 29 | 30 | {state.theme.showSocialLinks && ( 31 | <Box marginTop={10} paddingY={5}> 32 | <SocialMenu ml="0" menu={state.theme.socialLinks} /> 33 | </Box> 34 | )} 35 | </MenuDrawer> 36 | </> 37 | ); 38 | }; 39 | 40 | export default connect(Menu); 41 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/menu/menu-button.js: -------------------------------------------------------------------------------- 1 | import { Box } from "@chakra-ui/react"; 2 | import React, { forwardRef } from "react"; 3 | import { IoIosMenu } from "react-icons/io"; 4 | 5 | const MenuButton = forwardRef((props, ref) => ( 6 | <Box 7 | ref={ref} 8 | as="button" 9 | display={{ base: "flex", lg: "none" }} 10 | alignItems="center" 11 | justifyContent="center" 12 | flexShrink="0" 13 | mr={{ base: "auto", lg: "0" }} 14 | ml={{ base: "12px", sm: "0" }} 15 | {...props} 16 | > 17 | <Box boxSize={10} color="accent.400" as={IoIosMenu} /> 18 | </Box> 19 | )); 20 | 21 | export default MenuButton; 22 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/menu/menu-drawer.js: -------------------------------------------------------------------------------- 1 | import { 2 | Drawer, 3 | DrawerCloseButton, 4 | DrawerContent, 5 | DrawerOverlay 6 | } from "@chakra-ui/react"; 7 | import React from "react"; 8 | 9 | function MenuDrawer({ children, ...props }) { 10 | return ( 11 | <Drawer preserveScrollBarGap size="sm" placement="left" {...props}> 12 | <DrawerOverlay /> 13 | <DrawerContent bg="primary.700" px={8} max="auto"> 14 | <DrawerCloseButton color="white" /> 15 | {children} 16 | </DrawerContent> 17 | </Drawer> 18 | ); 19 | } 20 | 21 | export default MenuDrawer; 22 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/menu/menu-item.js: -------------------------------------------------------------------------------- 1 | import { Box } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import Link from "../link"; 4 | 5 | const MenuItem = ({ index, children, mb, link, ...rest }) => ( 6 | <Box as="li" listStyleType="none" mb={mb} {...rest}> 7 | <Link 8 | display="block" 9 | color="white" 10 | role="group" 11 | pos="relative" 12 | minH="40px" 13 | borderBottom="1px solid" 14 | borderColor="#ffffff14" 15 | _hover={{ 16 | bg: "rgba(236, 164, 25, 0.14)", 17 | borderColor: "accent.400", 18 | color: "accent.400" 19 | }} 20 | _focus={{ 21 | bg: "rgba(236, 164, 25, 0.14)", 22 | borderColor: "accent.400", 23 | color: "accent.400" 24 | }} 25 | transition="all 0.3s" 26 | padding="12px" 27 | link={link} 28 | > 29 | <Box 30 | as="span" 31 | pos="absolute" 32 | top="50%" 33 | transform="translateY(-50%)" 34 | fontSize="md" 35 | letterSpacing="-0.024" 36 | lineHeight="1.23" 37 | > 38 | {index} 39 | </Box> 40 | <Box 41 | paddingLeft={10} 42 | letterSpacing="-0.004em" 43 | lineHeight="1.39" 44 | fontWeight="medium" 45 | fontSize="24px" 46 | cursor="pointer" 47 | > 48 | {children} 49 | </Box> 50 | </Link> 51 | </Box> 52 | ); 53 | 54 | export default MenuItem; 55 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/nav.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect, styled } from "frontity"; 3 | import Link from "./link"; 4 | 5 | const Nav = ({ state }) => ( 6 | <Container> 7 | {state.theme.menu.map(([name, link]) => ( 8 | <Item key={name} isSelected={state.router.link === link}> 9 | <Link link={link}>{name}</Link> 10 | </Item> 11 | ))} 12 | </Container> 13 | ); 14 | 15 | export default connect(Nav); 16 | 17 | const Container = styled.nav` 18 | list-style: none; 19 | display: flex; 20 | width: 848px; 21 | max-width: 100%; 22 | box-sizing: border-box; 23 | padding: 0 24px; 24 | margin: 0; 25 | overflow-x: auto; 26 | `; 27 | 28 | const Item = styled.div` 29 | padding: 0; 30 | margin: 0 16px; 31 | color: #fff; 32 | font-size: 0.9em; 33 | box-sizing: border-box; 34 | flex-shrink: 0; 35 | 36 | & > a { 37 | display: inline-block; 38 | line-height: 2em; 39 | border-bottom: 2px solid 40 | ${({ isSelected }) => (isSelected ? "#fff" : "transparent")}; 41 | } 42 | 43 | &:first-of-type { 44 | margin-left: 0; 45 | } 46 | 47 | &:last-of-type { 48 | margin-right: 0; 49 | 50 | &:after { 51 | content: ""; 52 | display: inline-block; 53 | width: 24px; 54 | } 55 | } 56 | `; 57 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/newsletter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box, Heading, Text, Flex } from "@chakra-ui/react"; 3 | import { IoIosArrowRoundForward } from "react-icons/io"; 4 | import tile from "../assets/pattern-tile-green.svg"; 5 | 6 | export const PatternBox = ({ showPattern = true, ...props }) => ( 7 | <Box 8 | as="section" 9 | bg="primary.700" 10 | borderTop="10px solid" 11 | borderColor="accent.400" 12 | {...(showPattern && { 13 | bgImage: `url(${tile})`, 14 | bgSize: "1018px", 15 | bgPos: "top center" 16 | })} 17 | {...props} 18 | /> 19 | ); 20 | 21 | export const PatternBoxInner = props => ( 22 | <Box 23 | py="80px" 24 | position="relative" 25 | zIndex="1" 26 | overflow="hidden" 27 | textAlign="center" 28 | maxW="640px" 29 | mx="auto" 30 | px={6} 31 | {...props} 32 | /> 33 | ); 34 | 35 | // TODO: Add the logic to show this component based on if newsletter package exists 36 | export const Newsletter = props => ( 37 | <PatternBox {...props}> 38 | <Box 39 | py="80px" 40 | position="relative" 41 | zIndex="1" 42 | overflow="hidden" 43 | textAlign="center" 44 | maxW="640px" 45 | mx="auto" 46 | px={6} 47 | > 48 | <Heading color="white" textTransform="uppercase"> 49 | Never miss an update! 50 | </Heading> 51 | <Text mt={4} fontSize="xl" color="accent.400"> 52 | Receive the latest framework releases, new features, blog posts and 53 | tutorials. 54 | </Text> 55 | <SubscribeForm /> 56 | </Box> 57 | </PatternBox> 58 | ); 59 | 60 | const SubscribeInput = props => ( 61 | <Box 62 | as="input" 63 | width="100%" 64 | display="block" 65 | bg="white" 66 | height="60px" 67 | border="none" 68 | px="15px" 69 | fontSize="1.125rem" 70 | placeholder="Subscribe to our newsletter" 71 | _placeholder={{ color: "gray.500" }} 72 | {...props} 73 | /> 74 | ); 75 | 76 | const SubscribeButton = props => ( 77 | <Box 78 | as="button" 79 | transition="background-color ease .25s" 80 | bg="white" 81 | fontSize={{ base: "1.625rem" }} 82 | cursor="pointer" 83 | textAlign="center" 84 | color="accent.400" 85 | flexShrink="0" 86 | height="60px" 87 | minWidth="60px" 88 | display="flex" 89 | alignItems="center" 90 | justifyContent="center" 91 | _hover={{ 92 | bg: "accent.400", 93 | color: "white" 94 | }} 95 | _focus={{ 96 | bg: "accent.400", 97 | color: "white" 98 | }} 99 | {...props} 100 | > 101 | <Box 102 | focusable="false" 103 | width="40px" 104 | height="auto" 105 | as={IoIosArrowRoundForward} 106 | /> 107 | </Box> 108 | ); 109 | 110 | const SubscribeForm = props => ( 111 | <Flex as="form" mt="40px" {...props}> 112 | <SubscribeInput /> 113 | <SubscribeButton /> 114 | </Flex> 115 | ); 116 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/page404.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { styled } from "frontity"; 3 | 4 | const Page404 = () => ( 5 | <Container> 6 | <Title>Oops! 404 7 | 8 | That page can’t be found{" "} 9 | 10 | 😕 11 | 12 | 13 | 14 | ); 15 | 16 | export default Page404; 17 | 18 | const Container = styled.div` 19 | width: 800px; 20 | margin: 0; 21 | padding: 24px; 22 | text-align: center; 23 | `; 24 | 25 | const Title = styled.h1` 26 | margin: 0; 27 | margin-top: 24px; 28 | margin-bottom: 8px; 29 | color: rgba(12, 17, 43); 30 | font-size: 4em; 31 | `; 32 | 33 | const Description = styled.div` 34 | line-height: 1.6em; 35 | color: rgba(12, 17, 43, 0.8); 36 | margin: 24px 0; 37 | `; 38 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/post/author-bio.js: -------------------------------------------------------------------------------- 1 | import { Avatar, Box, Text, Flex } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import Link from "../link"; 4 | import { decode } from "frontity"; 5 | 6 | const AuthorBio = ({ image, description, name, link }) => ( 7 | 8 | 9 | 10 | 11 | 12 | by{" "} 13 | 19 | {decode(name)} 20 | 21 | 22 | {description && ( 23 | 24 | )} 25 | 26 | 27 | ); 28 | 29 | export default AuthorBio; 30 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/post/featured-media.js: -------------------------------------------------------------------------------- 1 | import { Box } from "@chakra-ui/react"; 2 | import Image from "@frontity/components/image"; 3 | import { connect } from "frontity"; 4 | import React from "react"; 5 | import { getMediaAttributes } from "../helpers"; 6 | 7 | // eslint-disable-next-line 8 | const FeaturedMedia = ({ state, actions, libraries, id, ...props }) => { 9 | const imgProps = getMediaAttributes(state, id); 10 | 11 | // is empty if the id doesn't exist in state.source anymore 12 | const noImgProps = Object.keys(imgProps).length === 0; 13 | 14 | if (noImgProps) return null; 15 | 16 | return ( 17 | 18 | 19 | 20 | ); 21 | }; 22 | 23 | export default connect(FeaturedMedia); 24 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/post/post-categories.js: -------------------------------------------------------------------------------- 1 | import { Box, Flex } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import Link from "../link"; 4 | import { decode } from "frontity"; 5 | 6 | export const PostCategory = props => ( 7 | 22 | ); 23 | 24 | export const PostCategories = ({ 25 | categories, 26 | limit = 3, 27 | color = "white", 28 | ...props 29 | }) => { 30 | const limitCategories = 31 | categories.length > limit 32 | ? categories.filter((_, idx) => idx < limit) 33 | : categories; 34 | 35 | return ( 36 | 37 | {limitCategories.map(category => ( 38 | 39 | 43 | 44 | ))} 45 | 46 | ); 47 | }; 48 | 49 | export default PostCategories; 50 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/post/post-header.js: -------------------------------------------------------------------------------- 1 | import { Box, Heading, Text } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import Link from "../link"; 4 | import PostCategories from "./post-categories"; 5 | import { formatDate } from "../helpers"; 6 | import { decode } from "frontity"; 7 | 8 | const PostHeader = ({ 9 | heading, 10 | categories, 11 | description, 12 | author, 13 | date, 14 | isPage, 15 | ...props 16 | }) => ( 17 | 18 | {categories && ( 19 | 24 | )} 25 | 33 | {description && {description}} 34 | {/* Don't show the author if we're on a page type */} 35 | {!isPage && author && ( 36 | 37 | by{" "} 38 | 39 | {decode(author.name)} 40 | 41 | 42 | )} 43 | {/* Don't show the date if we're on a page type */} 44 | {!isPage && date && ( 45 | 46 | {formatDate(date)} 47 | 48 | )} 49 | 50 | ); 51 | 52 | export default PostHeader; 53 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/post/post-progressbar.js: -------------------------------------------------------------------------------- 1 | import { Progress } from "@chakra-ui/react"; 2 | 3 | const PostProgressBar = props => ( 4 | ({ 15 | div: { 16 | backgroundColor: theme.colors.accent[400] 17 | } 18 | })} 19 | {...props} 20 | /> 21 | ); 22 | 23 | export default PostProgressBar; 24 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/post/post.js: -------------------------------------------------------------------------------- 1 | import { Box, Divider } from "@chakra-ui/react"; 2 | import { connect, styled } from "frontity"; 3 | import React, { useEffect } from "react"; 4 | import List from "../archive"; 5 | import useScrollProgress from "../hooks/useScrollProgress"; 6 | import { LightPatternBox } from "../styles/pattern-box"; 7 | import Section from "../styles/section"; 8 | import AuthorBio from "./author-bio"; 9 | import FeaturedMedia from "./featured-media"; 10 | import PostHeader from "./post-header"; 11 | import PostProgressBar from "./post-progressbar"; 12 | import { getPostData, formatPostData } from "../helpers"; 13 | 14 | const Post = ({ state, actions, libraries }) => { 15 | const postData = getPostData(state); 16 | const post = formatPostData(state, postData); 17 | 18 | // Get the html2react component. 19 | const Html2React = libraries.html2react.Component; 20 | 21 | // Once the post has loaded in the DOM, prefetch both the 22 | // home posts and the list component so if the user visits 23 | // the home page, everything is ready and it loads instantly. 24 | useEffect(() => { 25 | actions.source.fetch("/"); 26 | List.preload(); 27 | }, []); 28 | 29 | const [ref, scroll] = useScrollProgress(); 30 | 31 | // Load the post, but only if the data is ready. 32 | if (!postData.isReady) return null; 33 | 34 | return ( 35 | 36 | 37 | 46 | 47 | 48 | {!postData.isPage && } 49 | 50 | {/* Look at the settings to see if we should include the featured image */} 51 |
52 | {post.featured_media != null && ( 53 | 54 | )} 55 | 56 | {/* Render the content using the Html2React component so the HTML is processed 57 | by the processors we included in the libraries.html2react.processors array. */} 58 | 64 | 65 | 66 | 67 | 68 | 69 |
70 | 76 |
77 |
78 |
79 | ); 80 | }; 81 | 82 | export default connect(Post); 83 | 84 | // This component is the parent of the `content.rendered` HTML. We can use nested 85 | // selectors to style that HTML. 86 | const Content = styled.div` 87 | color: rgba(12, 17, 43, 0.8); 88 | word-break: break-word; 89 | 90 | * { 91 | max-width: 100%; 92 | } 93 | 94 | ul { 95 | padding: 1rem; 96 | } 97 | 98 | img { 99 | width: 100%; 100 | object-fit: cover; 101 | object-position: center; 102 | } 103 | 104 | figure { 105 | margin: 24px auto; 106 | /* next line overrides an inline style of the figure element. */ 107 | width: 100% !important; 108 | } 109 | 110 | iframe { 111 | display: block; 112 | margin: auto; 113 | } 114 | 115 | /* Input fields styles */ 116 | 117 | input[type="text"], 118 | input[type="email"], 119 | input[type="url"], 120 | input[type="tel"], 121 | input[type="number"], 122 | input[type="date"], 123 | textarea, 124 | select { 125 | display: block; 126 | padding: 6px 12px; 127 | font-size: 16px; 128 | font-weight: 400; 129 | line-height: 1.5; 130 | color: #495057; 131 | background-color: #fff; 132 | background-clip: padding-box; 133 | border: 1px solid #ced4da; 134 | border-radius: 4px; 135 | outline-color: transparent; 136 | transition: outline-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; 137 | margin: 8px 0 4px 0; 138 | 139 | &:focus { 140 | outline-color: #1f38c5; 141 | } 142 | } 143 | 144 | input[type="submit"] { 145 | display: inline-block; 146 | margin-bottom: 0; 147 | font-weight: 400; 148 | text-align: center; 149 | white-space: nowrap; 150 | vertical-align: middle; 151 | -ms-touch-action: manipulation; 152 | touch-action: manipulation; 153 | cursor: pointer; 154 | background-image: none; 155 | border: 1px solid #1f38c5; 156 | padding: 12px 36px; 157 | font-size: 14px; 158 | line-height: 1.42857143; 159 | border-radius: 4px; 160 | color: #fff; 161 | background-color: #1f38c5; 162 | } 163 | 164 | /* WordPress Core Align Classes */ 165 | 166 | @media (min-width: 420px) { 167 | img.aligncenter, 168 | img.alignleft, 169 | img.alignright { 170 | width: auto; 171 | } 172 | 173 | .aligncenter { 174 | display: block; 175 | margin-left: auto; 176 | margin-right: auto; 177 | } 178 | 179 | .alignright { 180 | float: right; 181 | margin-left: 24px; 182 | } 183 | 184 | .alignleft { 185 | float: left; 186 | margin-right: 24px; 187 | } 188 | } 189 | `; 190 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/search/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./search-results"; 2 | export { default as SearchButton } from "./search-button"; 3 | export { default as SearchModal } from "./search-modal"; 4 | export { default as SearchForm } from "./search-form"; 5 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/search/search-button.js: -------------------------------------------------------------------------------- 1 | import { Box } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import { IoIosSearch } from "react-icons/io"; 4 | 5 | const SearchButton = props => ( 6 | 20 | 21 | 22 | ); 23 | 24 | export default SearchButton; 25 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/search/search-form.js: -------------------------------------------------------------------------------- 1 | import { Flex, Icon, Input } from "@chakra-ui/react"; 2 | import { connect } from "frontity"; 3 | import React from "react"; 4 | import useSearch from "../hooks/useSearch"; 5 | 6 | // A11y: Add a hidden search button 7 | const SearchForm = props => { 8 | const { form, input } = useSearch(props); 9 | return ( 10 | 21 | 31 | 38 | 39 | ); 40 | }; 41 | 42 | export default connect(SearchForm); 43 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/search/search-modal.js: -------------------------------------------------------------------------------- 1 | import { Modal, ModalCloseButton, ModalContent } from "@chakra-ui/react"; 2 | import React from "react"; 3 | 4 | const SearchModal = props => ( 5 | 6 | 15 | {props.children} 16 | 17 | 18 | 19 | ); 20 | 21 | export default SearchModal; 22 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/search/search-results.js: -------------------------------------------------------------------------------- 1 | import { Box, Button, Heading, Input, Text, Stack } from "@chakra-ui/react"; 2 | import { connect } from "frontity"; 3 | import React from "react"; 4 | import Archive from "../archive"; 5 | import { PatternBox, PatternBoxInner } from "../newsletter"; 6 | import useSearch from "../hooks/useSearch"; 7 | 8 | const SearchHeader = ({ label, title, ...props }) => ( 9 | 10 | 17 | {label} 18 | 19 | 20 | 28 | {title} 29 | 30 | 31 | ); 32 | 33 | const SearchResultTitle = ({ resultCount, query }) => ( 34 | 1 ? "results" : "result"} for`} 37 | /> 38 | ); 39 | 40 | const NoResultTitle = ({ query }) => ( 41 | 42 | ); 43 | 44 | const NoResultContent = props => ( 45 | 55 | ); 56 | 57 | const SearchForm = connect(props => { 58 | const { form, input } = useSearch(props); 59 | return ( 60 | 61 | 68 | 71 | 72 | ); 73 | }); 74 | 75 | export const SearchResults = ({ state }) => { 76 | const data = state.source.get(state.router.link); 77 | 78 | // Get the total pages that match the current path/url 79 | const isEmpty = data.total === 0; 80 | 81 | return ( 82 | 83 | 88 | 89 | {isEmpty ? ( 90 | 91 | ) : ( 92 | 96 | )} 97 | 98 | 99 | 100 | {isEmpty ? ( 101 | 102 | 103 | 104 | We could not find any results for your search. You can give it 105 | another try through the search form below. 106 | 107 | 108 | 109 | 110 | ) : ( 111 | 112 | )} 113 | 114 | ); 115 | }; 116 | 117 | export default connect(SearchResults); 118 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/styles/border-box.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box } from "@chakra-ui/react"; 3 | 4 | /** 5 | * A Box with accented top border 6 | * @param {React.ComponentProps} props 7 | */ 8 | const BorderBox = props => ( 9 | 16 | ); 17 | 18 | export default BorderBox; 19 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/styles/font-face.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Global, css } from "frontity"; 3 | import kelsonBoldWOFF from "../../fonts/KelsonSans-Bold.woff"; 4 | import kelsonBoldTTF from "../../fonts/KelsonSans-Bold.ttf"; 5 | 6 | const FontFace = () => ( 7 | 18 | ); 19 | 20 | export default FontFace; 21 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/styles/pattern-box.js: -------------------------------------------------------------------------------- 1 | import { Box } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import Section from "./section"; 4 | import tileGreen from "../../assets/pattern-tile-green.svg"; 5 | import tileLight from "../../assets/pattern-tile-light-fade.svg"; 6 | 7 | /** 8 | * @param {React.ComponentProps} props 9 | */ 10 | export const PatternBox = ({ showPattern = true, ...props }) => ( 11 | 23 | ); 24 | 25 | /** 26 | * @param {React.ComponentProps} props 27 | */ 28 | export const PatternBoxInner = props => ( 29 |
39 | ); 40 | 41 | export const LightPatternBox = React.forwardRef( 42 | ({ showPattern = true, ...props }, ref) => ( 43 | 67 | ) 68 | ); 69 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/styles/processors.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Alert, Text, Box, Heading } from "@chakra-ui/react"; 3 | import Link from "../link"; 4 | 5 | /** 6 | * 7 | * @param {React.ElementType} tag 8 | * @param {{props: (nodeProps: Object) => Object, component: React.ComponentType}} options 9 | */ 10 | function makeProcessor(tag, options) { 11 | return { 12 | name: tag, 13 | test: ({ node }) => node.component === tag, 14 | processor: ({ node }) => { 15 | node.component = options.component || node.tag; 16 | node.props = { ...options.props({ node }), ...node.props }; 17 | return node; 18 | }, 19 | // allow for overriding this processors 20 | priority: 20 21 | }; 22 | } 23 | 24 | const blockquote = makeProcessor("blockquote", { 25 | props: () => ({ 26 | variant: "left-accent", 27 | status: "warning", 28 | marginY: "20px" 29 | }), 30 | component: Alert 31 | }); 32 | 33 | const paragraph = makeProcessor("p", { 34 | props: ({ node }) => { 35 | // we don't want to add marginTop if the paragraph is nested in another component 36 | const hasParent = Boolean(node.parent); 37 | return { 38 | marginTop: hasParent ? "0" : "10px", 39 | fontSize: { base: "md", md: "lg" }, 40 | lineHeight: "tall" 41 | }; 42 | }, 43 | component: Text 44 | }); 45 | 46 | const figcaption = makeProcessor("figcaption", { 47 | props: () => ({ 48 | as: "figcaption", 49 | fontSize: "1rem", 50 | marginTop: "20px", 51 | textAlign: "center", 52 | opacity: 0.8, 53 | marginBottom: "40px" 54 | }), 55 | component: Box 56 | }); 57 | 58 | // make for h1, h2, h4, h5, h6 59 | const h3 = makeProcessor("h3", { 60 | props: () => ({ 61 | as: "h3", 62 | marginTop: "40px", 63 | fontSize: { base: "lg", md: "xl" }, 64 | textTransform: "uppercase" 65 | }), 66 | component: Heading 67 | }); 68 | 69 | const PostLink = ({ children, href, rel, ...props }) => ( 70 | 79 | 80 | {children} 81 | 82 | 83 | ); 84 | 85 | const a = makeProcessor("a", { 86 | props: ({ node }) => node.props, 87 | component: PostLink 88 | }); 89 | 90 | const processors = [blockquote, paragraph, figcaption, h3, a]; 91 | export default processors; 92 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/styles/section.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box } from "@chakra-ui/react"; 3 | 4 | const sizes = { 5 | xs: "640px", 6 | sm: "700px", 7 | md: "750px", 8 | lg: "1150px", 9 | huge: "1550px", 10 | max: "2560px" 11 | }; 12 | 13 | /** 14 | * @param {React.ComponentProps} props 15 | */ 16 | const Section = ({ size = "md", ...props }) => ( 17 | 18 | ); 19 | export default Section; 20 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/components/title.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Head, connect } from "frontity"; 3 | import { decode } from "frontity"; 4 | 5 | const Title = ({ state }) => { 6 | // Get data about the current URL. 7 | const data = state.source.get(state.router.link); 8 | // Set the default title. 9 | let title = state.frontity.title; 10 | 11 | if (data.isTaxonomy) { 12 | // Add titles to taxonomies, like "Category: Nature - Blog Name" or "Tag: Japan - Blog Name". 13 | // 1. Get the taxonomy entity from the state to get its taxonomy term and name. 14 | const { taxonomy, name } = state.source[data.taxonomy][data.id]; 15 | // 2. Uppercase first letter of the taxonomy term (from "category" to "Category"). 16 | const taxonomyCapitalized = 17 | taxonomy.charAt(0).toUpperCase() + taxonomy.slice(1); 18 | // 3. Render the proper title. 19 | title = `${taxonomyCapitalized}: ${decode(name)} - ${state.frontity.title}`; 20 | } else if (data.isAuthor) { 21 | // Add titles to authors, like "Author: Jon Snow - Blog Name". 22 | // 1. Get the author entity from the state to get its name. 23 | const { name } = state.source.author[data.id]; 24 | // 2. Render the proper title. 25 | title = `Author: ${decode(name)} - ${state.frontity.title}`; 26 | } else if (data.isPostType) { 27 | // Add titles to posts and pages, using the title and ending with the Blog Name. 28 | // 1. Get the post entity from the state and get its title. 29 | const postTitle = state.source[data.type][data.id].title.rendered; 30 | // 2. Remove any HTML tags found in the title. 31 | const cleanTitle = postTitle.replace(/<\/?[^>]+(>|$)/g, ""); 32 | // 3. Render the proper title. 33 | title = `${cleanTitle} - ${state.frontity.title}`; 34 | } else if (data.is404) { 35 | // Add titles to 404's. 36 | title = `404 Not Found - ${state.frontity.title}`; 37 | } 38 | 39 | return ( 40 | 41 | {title} 42 | 43 | ); 44 | }; 45 | 46 | export default connect(Title); 47 | -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/fonts/KelsonSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chakra-ui/frontity-chakra-ui-theme/d78956a45874292c6c15e333cda098b4a273de40/packages/frontity-chakra-theme/src/fonts/KelsonSans-Bold.ttf -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/fonts/KelsonSans-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chakra-ui/frontity-chakra-ui-theme/d78956a45874292c6c15e333cda098b4a273de40/packages/frontity-chakra-theme/src/fonts/KelsonSans-Bold.woff -------------------------------------------------------------------------------- /packages/frontity-chakra-theme/src/index.js: -------------------------------------------------------------------------------- 1 | import Theme from "./components"; 2 | import image from "@frontity/html2react/processors/image"; 3 | import processors from "./components/styles/processors"; 4 | // import { theme } from "@chakra-ui/react"; 5 | 6 | const chakraTheme = { 7 | name: "frontity-chakra-theme", 8 | roots: { 9 | // In Frontity, any package can add React components to the site. 10 | // We use roots for that, scoped to the "theme" namespace. 11 | theme: Theme 12 | }, 13 | state: { 14 | // State is where the packages store their default settings and other 15 | // relevant state. It is scoped to the "theme" namespace. 16 | theme: { 17 | /** 18 | * The logo can be a text or an image url 19 | * logo : "Frontity" 20 | * logo: "https://uploads-ssl.webflow.com/5be00771820599586e6bd032/5be0223588110a6dbcac2d05_image.svg", 21 | */ 22 | logo: "Frontity", 23 | showBackgroundPattern: true, 24 | showSocialLinks: true, 25 | /** 26 | * socialLinks: [ 27 | ["pinterest", "https://www.pinterest.com/frontity/"], 28 | ["facebook", "https://www.instagram.com/frontity/"], 29 | ["twitter", "https://www.twitter.com/frontity/"] 30 | ], 31 | */ 32 | socialLinks: [], 33 | menu: [], 34 | featured: { 35 | showOnArchive: false, 36 | showOnPost: true 37 | }, 38 | 39 | colors: { 40 | primary: { 41 | 50: "#e9f5f2", 42 | 100: "#d4dcd9", 43 | 200: "#bbc3be", 44 | 300: "#a1aba5", 45 | 400: "#87938b", 46 | 500: "#6d7972", 47 | 600: "#555f58", 48 | 700: "#323c34", 49 | 800: "#232924", 50 | 900: "#272727" 51 | }, 52 | accent: { 53 | 50: "#ede4d3", 54 | 100: "#fbe3b2", 55 | 200: "#f6d086", 56 | 300: "#f1be58", 57 | 400: "#eca419", 58 | 500: "#d49212", 59 | 600: "#a5710b", 60 | 700: "#775105", 61 | 800: "#483100", 62 | 900: "#1d0f00" 63 | } 64 | }, 65 | isSearchModalOpen: false, 66 | isMobileMenuOpen: false, 67 | autoPreFetch: "all" 68 | } 69 | }, 70 | // Actions are functions that modify the state or deal with other parts of 71 | // Frontity like libraries. 72 | actions: { 73 | theme: { 74 | openMobileMenu: ({ state }) => { 75 | state.theme.isMobileMenuOpen = true; 76 | }, 77 | closeMobileMenu: ({ state }) => { 78 | state.theme.isMobileMenuOpen = false; 79 | }, 80 | openSearchModal: ({ state }) => { 81 | state.theme.isSearchModalOpen = true; 82 | }, 83 | closeSearchModal: ({ state }) => { 84 | state.theme.isSearchModalOpen = false; 85 | } 86 | } 87 | }, 88 | libraries: { 89 | html2react: { 90 | // Add a processor to html2react so it processes the tags 91 | // inside the content HTML. You can add your own processors too. 92 | processors: [image, ...processors] 93 | } 94 | } 95 | }; 96 | 97 | export default chakraTheme; 98 | -------------------------------------------------------------------------------- /sandbox.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "template": "node" 3 | } 4 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "alias": "frontity-chakra", 4 | "builds": [ 5 | { 6 | "src": "package.json", 7 | "use": "@frontity/now" 8 | } 9 | ] 10 | } 11 | --------------------------------------------------------------------------------