├── .babelrc ├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── .npmrc ├── .prettierrc ├── LICENSE ├── README.md ├── edit-guide.md ├── gatsby-browser.js ├── gatsby-config.js ├── gatsby-node.js ├── package.json ├── src ├── assets │ └── img │ │ ├── 404.png │ │ ├── dillion.jpg │ │ ├── esther.jpg │ │ ├── logo.png │ │ └── main.jpg ├── components │ ├── Author │ │ ├── Author.js │ │ ├── Author.module.scss │ │ └── AuthorList.js │ ├── Blog │ │ ├── Common │ │ │ ├── DiscussArticle.js │ │ │ └── EditArticle.js │ │ ├── RelatedArticles │ │ │ └── RelatedArticles.js │ │ ├── Saved │ │ │ └── Saved.js │ │ ├── Styles │ │ │ ├── IndividualPost.module.scss │ │ │ ├── Post.module.scss │ │ │ └── Posts.module.scss │ │ ├── SuggestArticles │ │ │ ├── SuggestArticles.js │ │ │ └── SuggestArticles.module.scss │ │ ├── Tags │ │ │ └── Tags.js │ │ ├── Templates │ │ │ ├── IndividualPost.js │ │ │ ├── Post.js │ │ │ └── Posts.js │ │ └── Views │ │ │ ├── CategoryPage.js │ │ │ └── TagPage.js │ ├── Brand │ │ └── Details.js │ ├── Footer │ │ ├── Footer.js │ │ └── Footer.module.scss │ ├── Header │ │ ├── Header.js │ │ └── Header.module.scss │ ├── Helmet.js │ ├── Nav │ │ ├── Drawer │ │ │ ├── Drawer.js │ │ │ └── Drawer.module.scss │ │ ├── NavLink.js │ │ └── NavLinks.js │ ├── Newsletter │ │ ├── Newsletter.js │ │ └── Newsletter.module.scss │ ├── Not5YearOldLiterallyNote │ │ └── index.tsx │ ├── SocialMedia │ │ ├── Media.js │ │ ├── ShareArticle.js │ │ └── ShareArticle.module.scss │ └── UI │ │ ├── Backdrop │ │ ├── Backdrop.js │ │ └── Backdrop.module.scss │ │ ├── Icons.js │ │ └── Theme │ │ ├── Theme.js │ │ └── Theme.module.scss ├── containers │ ├── Layout │ │ ├── Layout.js │ │ └── Layout.module.scss │ └── Search │ │ ├── Search.js │ │ └── Search.module.scss ├── functions │ └── dateFormatter.js ├── hooks │ └── allCategories.js ├── pages │ ├── 404.js │ ├── about.js │ ├── author │ │ ├── dillionmegida.js │ │ └── ejidikeesther.js │ ├── categories.js │ ├── p │ │ ├── css │ │ │ ├── column-count │ │ │ │ ├── example.png │ │ │ │ └── index.md │ │ │ ├── css-animations.md │ │ │ ├── css-measurements.md │ │ │ ├── css-selector-methods.md │ │ │ ├── css-specificity.md │ │ │ └── what-is-css.md │ │ ├── general │ │ │ ├── frontend-and-backend-development.md │ │ │ ├── how-websites-are-accessed-with-domain-names.md │ │ │ ├── introduction-to-cors.md │ │ │ ├── introduction-to-the-web.md │ │ │ ├── library-vs-framework.md │ │ │ ├── website-and-web-applications.md │ │ │ ├── what-are-scripting-languages.md │ │ │ └── what-are-static-site-generators │ │ │ │ ├── fetch-from-server.png │ │ │ │ └── index.md │ │ ├── git │ │ │ └── introduction-to-git.md │ │ ├── html │ │ │ ├── document-fragment-and-dom │ │ │ │ └── index.md │ │ │ ├── file-input-in-depth.md │ │ │ ├── noopener-noreferrer.md │ │ │ ├── semantic-html.md │ │ │ └── what-is-html.md │ │ └── javascript │ │ │ ├── async-await.md │ │ │ ├── call-bind-apply-javascript.md │ │ │ ├── call-stack.md │ │ │ ├── callback-queue-and-event-loop.md │ │ │ ├── functions-are-also-objects │ │ │ ├── index.md │ │ │ └── properties-of-function.png │ │ │ ├── javascript-promises.md │ │ │ ├── object-dot-create.md │ │ │ ├── package-vs-package-lock-json │ │ │ ├── index.md │ │ │ ├── package-json.png │ │ │ └── package-lock-json.png │ │ │ ├── parameters-and-arguments.md │ │ │ ├── rest-and-spread-operator-javascript.md │ │ │ ├── sort-array-method.md │ │ │ ├── string-substr-and-slice-method.md │ │ │ ├── the-dom.md │ │ │ ├── var-let-const.md │ │ │ └── what-is-javascript.md │ ├── saved.js │ └── search.js └── styles │ ├── 404.module.scss │ ├── about.module.scss │ ├── categories.module.scss │ ├── global.css │ ├── index.module.scss │ └── search.module.scss ├── static └── admin │ └── config.yml └── test └── articles.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-proposal-class-properties", 4 | ["@babel/plugin-proposal-decorators", { "legacy": true }] 5 | ], 6 | "presets": [ 7 | [ 8 | "babel-preset-gatsby", 9 | { 10 | "targets": { 11 | "browsers": [">0.25%", "not dead"] 12 | } 13 | } 14 | ] 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [10.x, 12.x, 14.x] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - run: npm i mocha chai 25 | - run: npm test 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (http://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # Typescript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # dotenv environment variables file 57 | .env 58 | 59 | # gatsby files 60 | .cache/ 61 | public 62 | 63 | # Mac files 64 | .DS_Store 65 | 66 | # Yarn 67 | yarn-error.log 68 | .pnp/ 69 | .pnp.js 70 | # Yarn Integrity file 71 | .yarn-integrity -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": false, 4 | "singleQuote": false, 5 | "tabWidth": 2, 6 | "trailingComma": "es5" 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 TheWebFor5 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TheWebFor5 2 | 3 | This is a blog platform founded by [Dillion Megida](https://twitter.com/iamdillion) for demystifying web development topics. This hopes to be different from other blog platforms such that the topics aren't entirely explained in technical terms but simpler ones that can be easily consumed by a reader. 4 | 5 | ## License 6 | 7 | This project is open-source and available under the [MIT License](https://github.com/dillionmegida/thewebfor5/blob/master/LICENSE). 8 | 9 | ## Contributions 10 | 11 | If you find any bug or feature request while using the web application, kindly [create an issue](https://github.com/dillionmegida/thewebfor5/issues) on it. 12 | 13 | [Pull Requests](https://github.com/dillionmegida/thewebfor5/pulls) are also welcome. 14 | 15 | You can reach us on twitter - [@thewebfor5](https://twitter.com/thewebfor5). 16 | -------------------------------------------------------------------------------- /edit-guide.md: -------------------------------------------------------------------------------- 1 | # Guide for easily editing the site 2 | 3 | ## Add new Categories 4 | 5 | 1. Add category to netlify cms by editing the config file - static/adming/config.yml. 6 | 2. Add category to Blog Categories - src/components/blog/categories/[category].js. Then, state the category name. 7 | 3. Add category page to src/pages/category/[category].js 8 | 9 | ## Add new Author 10 | 11 | 1. Add the author details in the author list - /src/components/Author/AuthorList.js 12 | 2. The slug which you'd use for the author should be used to create a new file in src/pages/author - [authorSlug].js 13 | 3. In the new file, import the author component and pass a prop of the authorID 14 | -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | import "./src/styles/global.css" 2 | require("prismjs/themes/prism-tomorrow.css") 3 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteMetadata: { 3 | siteUrl: `https://thewebfor5.com`, 4 | }, 5 | 6 | plugins: [ 7 | // Styled Components 8 | `gatsby-plugin-styled-components`, 9 | 10 | // Sass 11 | `gatsby-plugin-sass`, 12 | 13 | // For NetlifyCMS 14 | `gatsby-plugin-netlify-cms`, 15 | 16 | // Automatic sitemaps when built 17 | { 18 | resolve: `gatsby-plugin-sitemap`, 19 | options: { 20 | exclude: ["/tags/*", "/search", "/categories"], 21 | }, 22 | }, 23 | 24 | // React Helmet for populating thehead tag 25 | `gatsby-plugin-react-helmet`, 26 | 27 | // For handling file sources 28 | { 29 | resolve: `gatsby-source-filesystem`, 30 | options: { 31 | name: `src`, 32 | path: `${__dirname}/src/`, 33 | }, 34 | }, 35 | 36 | // For transforming markdowns 37 | `gatsby-plugin-sharp`, 38 | { 39 | resolve: `gatsby-transformer-remark`, 40 | options: { 41 | plugins: [ 42 | { 43 | resolve: `gatsby-remark-autolink-headers`, 44 | options: { 45 | offsetY: "70", 46 | }, 47 | }, 48 | { 49 | resolve: `gatsby-remark-images`, 50 | options: { 51 | maxWidth: 800, 52 | }, 53 | }, 54 | `gatsby-remark-liquid-tags`, 55 | { 56 | resolve: `gatsby-remark-prismjs`, 57 | options: { 58 | classPrefix: "language-", 59 | inlineCodeMarker: null, 60 | aliases: {}, 61 | showLineNumbers: false, 62 | noInlineHightlight: false, 63 | languageExtensions: [ 64 | { 65 | language: "superscript", 66 | extend: "javascript", 67 | definition: { 68 | superscript_types: /(SuperType)/, 69 | }, 70 | insertBefore: { 71 | function: { 72 | superscript_keywords: /(superif|superelse)/, 73 | }, 74 | }, 75 | }, 76 | ], 77 | }, 78 | }, 79 | ], 80 | }, 81 | }, 82 | 83 | // For google analytics 84 | { 85 | resolve: `gatsby-plugin-gtag`, 86 | options: { 87 | // your google analytics tracking id 88 | trackingId: "UA-148541646-4", 89 | // Puts tracking script in the head instead of the body 90 | head: true, 91 | // enable ip anonymization 92 | anonymize: true, 93 | }, 94 | }, 95 | 96 | // For gatsby manifest 97 | { 98 | resolve: `gatsby-plugin-manifest`, 99 | options: { 100 | name: `TheWebFor5`, 101 | short_name: `TheWebFor5`, 102 | start_url: `/`, 103 | background_color: `#262625`, 104 | theme_color: `#262625`, 105 | display: `standalone`, 106 | icon: `src/assets/img/logo.png`, 107 | }, 108 | }, 109 | 110 | // Gatsby offline 111 | `gatsby-plugin-offline`, 112 | ], 113 | } 114 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | const _ = require("lodash") 3 | const { createFilePath } = require("gatsby-source-filesystem") 4 | const createPaginatedPages = require("gatsby-paginate") 5 | 6 | exports.onCreateNode = ({ node, getNode, actions }) => { 7 | const { createNodeField } = actions 8 | if (node.internal.type == "MarkdownRemark") { 9 | const slug = createFilePath({ node, getNode, basePath: "pages" }) 10 | createNodeField({ 11 | node, 12 | name: "slug", 13 | value: slug, 14 | }) 15 | } 16 | } 17 | 18 | exports.createPages = async ({ graphql, actions, reporter }) => { 19 | const { createPage } = actions 20 | const result = await graphql(` 21 | query { 22 | allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) { 23 | edges { 24 | node { 25 | id 26 | timeToRead 27 | frontmatter { 28 | title 29 | category 30 | cover 31 | date 32 | pageDescription 33 | } 34 | fields { 35 | slug 36 | } 37 | } 38 | } 39 | } 40 | tagsGroup: allMarkdownRemark(limit: 2000) { 41 | group(field: frontmatter___tags) { 42 | fieldValue 43 | } 44 | } 45 | categoriesGroup: allMarkdownRemark { 46 | group(field: frontmatter___category) { 47 | fieldValue 48 | } 49 | } 50 | } 51 | `) 52 | 53 | if (result.errors) { 54 | reporter.panicOnBuild(`Error while running GraphQL query.`) 55 | return 56 | } 57 | 58 | result.data.allMarkdownRemark.edges.forEach(({ node }) => { 59 | // let { title } = node.frontmatter; 60 | // imgPath = './images'; 61 | // const imageSlug = createImage(title, imgPath); 62 | 63 | createPage({ 64 | path: node.fields.slug, 65 | component: path.resolve( 66 | "./src/components/Blog/Templates/IndividualPost.js" 67 | ), 68 | context: { 69 | slug: node.fields.slug, 70 | }, 71 | }) 72 | }) 73 | 74 | createPaginatedPages({ 75 | edges: result.data.allMarkdownRemark.edges, 76 | createPage: createPage, 77 | pageTemplate: "./src/components/Blog/Templates/Posts.js", 78 | pageLength: 10, // This is optional and defaults to 10 if not used 79 | pathPrefix: "", // This is optional and defaults to an empty string if not used 80 | context: {}, // This is optional and defaults to an empty object if not used 81 | }) 82 | 83 | result.data.tagsGroup.group.forEach((tag) => { 84 | createPage({ 85 | path: `tags/${_.kebabCase(tag.fieldValue)}/`, 86 | component: path.resolve(`./src/components/Blog/Views/TagPage.js`), 87 | context: { 88 | tag: tag.fieldValue, 89 | }, 90 | }) 91 | }) 92 | 93 | result.data.categoriesGroup.group.forEach((category) => { 94 | createPage({ 95 | path: `category/${_.kebabCase(category.fieldValue)}/`, 96 | component: path.resolve(`./src/components/Blog/Views/CategoryPage.js`), 97 | context: { 98 | category: category.fieldValue, 99 | }, 100 | }) 101 | }) 102 | } 103 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thewebfor5", 3 | "private": true, 4 | "description": "A website that simplifies web topics", 5 | "version": "0.1.0", 6 | "license": "MIT", 7 | "scripts": { 8 | "build": "gatsby build", 9 | "develop": "gatsby develop", 10 | "format": "prettier --write src/**/*.{js,jsx}", 11 | "start": "npm run develop", 12 | "serve": "gatsby serve", 13 | "test": "mocha" 14 | }, 15 | "dependencies": { 16 | "babel-plugin-styled-components": "^1.12.0", 17 | "gatsby": "^2.18.11", 18 | "gatsby-paginate": "^1.1.1", 19 | "gatsby-plugin-google-analytics": "^2.1.17", 20 | "gatsby-plugin-gtag": "^1.0.11", 21 | "gatsby-plugin-manifest": "^2.2.22", 22 | "gatsby-plugin-netlify-cms": "^4.1.33", 23 | "gatsby-plugin-offline": "^3.0.15", 24 | "gatsby-plugin-react-helmet": "^3.1.18", 25 | "gatsby-plugin-sass": "^2.1.26", 26 | "gatsby-plugin-sharp": "^2.5.6", 27 | "gatsby-plugin-sitemap": "^2.2.10", 28 | "gatsby-plugin-styled-components": "^3.10.0", 29 | "gatsby-remark-autolink-headers": "^2.3.1", 30 | "gatsby-remark-images": "^3.2.5", 31 | "gatsby-remark-liquid-tags": "^1.1.6", 32 | "gatsby-remark-prismjs": "^3.3.27", 33 | "gatsby-source-filesystem": "^2.1.42", 34 | "gatsby-transformer-remark": "^2.6.42", 35 | "netlify-cms-app": "^2.9.7", 36 | "node-sass": "^4.13.0", 37 | "prismjs": "^1.17.1", 38 | "prop-types": "^15.7.2", 39 | "react": "^16.8.6", 40 | "react-dom": "^16.8.6", 41 | "react-helmet": "^5.2.1", 42 | "styled-components": "^5.2.1" 43 | }, 44 | "devDependencies": { 45 | "@babel/plugin-proposal-decorators": "^7.12.13", 46 | "@types/styled-components": "^5.1.7", 47 | "babel-plugin-transform-class-properties": "^6.24.1", 48 | "babel-preset-gatsby": "^0.12.0", 49 | "chai": "^4.2.0", 50 | "ghooks": "^2.0.4", 51 | "mocha": "^8.0.1", 52 | "prettier": "^1.18.2" 53 | }, 54 | "config": { 55 | "ghooks": { 56 | "pre-commit": "npm run test" 57 | } 58 | }, 59 | "repository": { 60 | "type": "git", 61 | "url": "https://github.com/dillionmegida/thewebfor5" 62 | }, 63 | "bugs": { 64 | "url": "https://github.com/dillionmegida/thewebfor5/issues" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/assets/img/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dillionmegida/thewebfor5/214a67edffb2c4d2a5d26b686d5e10173274f561/src/assets/img/404.png -------------------------------------------------------------------------------- /src/assets/img/dillion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dillionmegida/thewebfor5/214a67edffb2c4d2a5d26b686d5e10173274f561/src/assets/img/dillion.jpg -------------------------------------------------------------------------------- /src/assets/img/esther.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dillionmegida/thewebfor5/214a67edffb2c4d2a5d26b686d5e10173274f561/src/assets/img/esther.jpg -------------------------------------------------------------------------------- /src/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dillionmegida/thewebfor5/214a67edffb2c4d2a5d26b686d5e10173274f561/src/assets/img/logo.png -------------------------------------------------------------------------------- /src/assets/img/main.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dillionmegida/thewebfor5/214a67edffb2c4d2a5d26b686d5e10173274f561/src/assets/img/main.jpg -------------------------------------------------------------------------------- /src/components/Author/Author.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "./Author.module.scss" 3 | 4 | import AllAuthors from "./AuthorList" 5 | import Layout from "../../containers/Layout/Layout" 6 | import Brand from "../Brand/Details" 7 | import { Twitter, Web } from "../UI/Icons" 8 | import { StaticQuery, graphql } from "gatsby" 9 | 10 | export default (props) => { 11 | const authorID = props.authorID 12 | const filteredAuthor = AllAuthors.filter( 13 | (author) => author.authorID === authorID 14 | ) 15 | 16 | const [author] = filteredAuthor 17 | 18 | return ( 19 | { 42 | const authorArticles = data.allPosts.edges.filter( 43 | ({ node }) => node.frontmatter.authorID === author.authorID 44 | ) 45 | const noOfArticles = authorArticles.length 46 | 47 | return ( 48 |
49 |

{author.name}

50 |

Author at {Brand.name}

51 |
52 | {author.name} 53 |
54 |
55 |

56 | Stack:
57 | {author.skills.map( 58 | (skill, index) => 59 | `${skill}${ 60 | index !== author.skills.length - 1 ? ", " : "" 61 | }` 62 | )} 63 |

64 |

65 | {author.twitter ? ( 66 | 70 | 71 | 72 | ) : null} 73 | {author.website ? ( 74 | 78 | 79 | 80 | ) : null} 81 |

82 |

83 | {noOfArticles} article{noOfArticles === 1 ? "" : "s"}{" "} 84 | written. 85 |

86 |

87 | Bio:
88 | {author.bio} 89 |

90 |
91 |
92 | ) 93 | }} 94 | /> 95 | } 96 | /> 97 | ) 98 | } 99 | -------------------------------------------------------------------------------- /src/components/Author/Author.module.scss: -------------------------------------------------------------------------------- 1 | .Author { 2 | width: 100%; 3 | display: flex; 4 | align-items: center; 5 | flex-flow: column; 6 | 7 | & h1 { 8 | font-size: 30px; 9 | margin: 4px 0; 10 | color: var(--color3); 11 | 12 | & ~ p { 13 | font-size: 14px; 14 | margin: 4px 0; 15 | color: var(--color3); 16 | } 17 | } 18 | 19 | & .Img { 20 | width: 200px; 21 | height: 200px; 22 | border-radius: 50%; 23 | overflow: hidden; 24 | border: 5px solid var(--color5); 25 | 26 | & img { 27 | width: 100%; 28 | object-fit: cover; 29 | } 30 | } 31 | 32 | & .Details { 33 | text-align: center; 34 | padding: 10px; 35 | width: 100%; 36 | border: 2px solid var(--color5); 37 | margin: 5px 0; 38 | color: var(--color3); 39 | 40 | & p { 41 | margin: 10px 0; 42 | } 43 | 44 | & p:last-child { 45 | /* The paragraph for bio */ 46 | max-width: 400px; 47 | margin: 0 auto; 48 | } 49 | 50 | & a { 51 | color: var(--color2); 52 | padding: 2px; 53 | margin: 0 4px; 54 | 55 | &:hover, 56 | &:focus { 57 | outline: 1px solid var(--color2); 58 | } 59 | } 60 | } 61 | } 62 | 63 | @media only screen and (max-width: 400px) { 64 | .Author { 65 | & h1 { 66 | font-size: 20px; 67 | } 68 | 69 | & .Img { 70 | width: 130px; 71 | height: 130px; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/components/Author/AuthorList.js: -------------------------------------------------------------------------------- 1 | // Everything is static, so ensure that authorID is unique for every author 2 | 3 | const authorList = [ 4 | { 5 | authorID: 1, 6 | slug: "dillionmegida", 7 | name: "Dillion Megida", 8 | twitter: "iamdillion", 9 | img: require("../../assets/img/dillion.jpg"), 10 | skills: ["HTML5", "CSS", "Javascript", "ReactJS", "GatsbyJS"], 11 | bio: 12 | "Dillion is a front-end developer and technical writer. He loves teaching what he learns in the simplest ways he can", 13 | website: "https://dillionmegida.com", 14 | }, 15 | { 16 | authorID: 2, 17 | slug: "ejidikeesther", 18 | name: "Ejidike Esther", 19 | twitter: "lady_catheryn", 20 | img: require("../../assets/img/esther.jpg"), 21 | skills: ["HTML5", "CSS", "Javascript", "Bootstrap", "Git", "Wordpress CMS"], 22 | bio: 23 | "Front-end web developer. I listen to music or sometimes play chords on my guitar when I'm not coding. I also love teaching what I learn.", 24 | }, 25 | ] 26 | 27 | export default authorList 28 | -------------------------------------------------------------------------------- /src/components/Blog/Common/DiscussArticle.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Brand from "../../Brand/Details" 3 | 4 | export default (props) => ( 5 | 12 | Discuss on Twitter 13 | 14 | ) 15 | -------------------------------------------------------------------------------- /src/components/Blog/Common/EditArticle.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export default (props) => ( 4 | 12 | Edit Post on GitHub 13 | 14 | ) 15 | -------------------------------------------------------------------------------- /src/components/Blog/RelatedArticles/RelatedArticles.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import { StaticQuery, graphql } from "gatsby" 4 | import Post from "../Templates/Post" 5 | import formatBlogDate from "../../../functions/dateFormatter" 6 | 7 | export default (props) => ( 8 | { 33 | const { edges: AllPosts } = data.allPosts 34 | 35 | let RelatedArr = [] 36 | let tags = props.tags 37 | 38 | // ID is necessary to avoid using the same article in related articles 39 | let articleID = props.articleID 40 | 41 | // This provides a limit for the related articles 42 | let counter = 0 43 | let limit = 3 44 | 45 | AllPosts.forEach((post) => { 46 | tags.forEach((tag) => { 47 | if ( 48 | post.node.frontmatter.tags !== null && 49 | post.node.frontmatter.tags.includes(tag) 50 | ) { 51 | if ( 52 | !RelatedArr.includes(post.node) && 53 | // the same article should not display 54 | post.node.id !== articleID && 55 | counter < limit 56 | ) { 57 | RelatedArr.push(post.node) 58 | counter++ 59 | } 60 | } 61 | }) 62 | }) 63 | 64 | return ( 65 | 66 | {RelatedArr.length > 0 ? ( 67 |
68 | {RelatedArr.length > 0 &&

Related Articles

} 69 | {RelatedArr.map((post) => ( 70 | 1 ? "s" : ""} read`} 81 | /> 82 | ))} 83 |
84 | ) : null} 85 |
86 | ) 87 | }} 88 | /> 89 | ) 90 | -------------------------------------------------------------------------------- /src/components/Blog/Styles/IndividualPost.module.scss: -------------------------------------------------------------------------------- 1 | .Header { 2 | width: 100%; 3 | 4 | // & .Author { 5 | // font-size: 14px; 6 | // } 7 | 8 | & > h1 { 9 | font-size: 30px; 10 | color: var(--color2); 11 | margin-bottom: 3px; 12 | word-wrap: break-word; 13 | } 14 | 15 | & > p { 16 | color: var(--color3); 17 | margin: 4px 0 10px; 18 | font-size: 15px; 19 | } 20 | 21 | & > button { 22 | margin: 10px 0 15px; 23 | border: 0; 24 | background: none; 25 | cursor: pointer; 26 | padding: 3px; 27 | font-size: 15px; 28 | display: block; 29 | 30 | &.Unsave { 31 | color: var(--color2); 32 | } 33 | 34 | &.Save { 35 | color: var(--color4); 36 | } 37 | } 38 | 39 | & > span, 40 | & .Author { 41 | font-size: 14px; 42 | overflow-wrap: break-word; 43 | 44 | & a { 45 | color: var(--color4); 46 | padding: 3px; 47 | width: 100%; 48 | 49 | &:hover, 50 | &:focus { 51 | outline: 1px solid var(--color4); 52 | } 53 | } 54 | } 55 | } 56 | 57 | .LeftContents { 58 | border: 1px solid var(--color2); 59 | padding: 10px; 60 | border-radius: 5px; 61 | position: fixed; 62 | width: 200px; 63 | 64 | & > * { 65 | margin: 0; 66 | } 67 | 68 | & > h1 { 69 | font-size: 20px; 70 | margin: 0 0 15px; 71 | } 72 | } 73 | 74 | .PageHeader { 75 | display: none; 76 | width: 100%; 77 | margin: 0 auto; 78 | color: var(--color3); 79 | } 80 | 81 | .PostSection { 82 | width: 100%; 83 | margin: 20px auto 0; 84 | 85 | & .PostCover { 86 | width: 100%; 87 | 88 | & img { 89 | height: auto; 90 | width: 100%; 91 | } 92 | } 93 | 94 | & .PostContent { 95 | color: var(--color3); 96 | line-height: 25px; 97 | 98 | h2, h3, h4, h5, h6 { 99 | margin: 40px 0 10px; 100 | } 101 | 102 | .Html { 103 | margin-bottom: 40px; 104 | border-bottom: 1px solid var(--color5); 105 | } 106 | 107 | & div[class="gatsby-highlight"] { 108 | // margin: 0.5em 0; 109 | overflow: auto; 110 | 111 | & pre[class*="language-"] { 112 | // to control overlap start 113 | overflow: initial; 114 | float: left; 115 | // end 116 | min-width: 100%; 117 | margin: 0; 118 | border-radius: 0; 119 | } 120 | } 121 | 122 | & span[class="gatsby-highlight-code-line"] { 123 | background-color: rgb(68, 68, 68); 124 | display: block; 125 | border-left: 0.25em solid white; 126 | color: pink !important; 127 | margin-left: -1em; 128 | margin-right: -1em; 129 | padding: 0 1em 0 0.75em; 130 | } 131 | 132 | & div[class="TOC"] a { 133 | color: var(--color3); 134 | text-decoration: none; 135 | font-weight: normal; 136 | 137 | &:hover, 138 | :focus { 139 | outline-color: var(--color3); 140 | } 141 | } 142 | 143 | /* Offset links are necessary because there is a top fixed section before the content */ 144 | & a[class="offset_link"] { 145 | position: relative; 146 | display: block; 147 | visibility: hidden; 148 | top: -100px; 149 | } 150 | 151 | & h1 { 152 | /* This is essential to control every h1 tags from markdowns as there is already an initial h1 tag */ 153 | font-size: 25px; 154 | color: var(--color3); 155 | } 156 | } 157 | 158 | & a { 159 | color: var(--color4); 160 | padding: 3px; 161 | 162 | &:hover, 163 | &:focus { 164 | outline: 1px solid var(--color4); 165 | } 166 | } 167 | 168 | & ul { 169 | margin: 0; 170 | 171 | & li { 172 | list-style-type: disc; 173 | padding-left: 10px; 174 | } 175 | } 176 | 177 | & hr { 178 | width: 80%; 179 | margin: 0 auto; 180 | border: 0.5px solid var(--color5); 181 | } 182 | 183 | & code[class="language-text"] { 184 | border-radius: 0%; 185 | padding: 3px; 186 | } 187 | 188 | & blockquote { 189 | margin: 0 0 20px; 190 | padding: 5px 20px; 191 | border: 1px solid var(--color5); 192 | border-left-width: 20px; 193 | } 194 | } 195 | 196 | .Others { 197 | height: 300px; 198 | width: 95%; 199 | margin: 0 auto; 200 | background-color: pink; 201 | z-index: 30; 202 | } 203 | 204 | .EditDiscuss { 205 | width: 100%; 206 | display: flex; 207 | flex-wrap: wrap; 208 | 209 | & a { 210 | margin: 10px 5px; 211 | color: var(--color2); 212 | padding: 5px; 213 | 214 | &:hover, 215 | &:focus { 216 | outline: 1px solid var(--color2); 217 | } 218 | } 219 | } 220 | 221 | .RelatedArticles { 222 | border-top: 1px solid var(--color5); 223 | 224 | & > h2 { 225 | background-color: var(--color2); 226 | padding: 5px; 227 | // display: inl 228 | width: 170px; 229 | color: var(--color1); 230 | font-size: 20px; 231 | text-align: center; 232 | } 233 | } 234 | 235 | @media only screen and (max-width: 800px) { 236 | .PageHeader { 237 | display: block; 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/components/Blog/Styles/Post.module.scss: -------------------------------------------------------------------------------- 1 | .Post { 2 | width: 100%; 3 | margin: 20px 0; 4 | height: min-content; 5 | border: 1px solid var(--color2); 6 | padding: 20px; 7 | border-radius: 5px; 8 | display: flex; 9 | justify-content: center; 10 | // align-content: center; 11 | 12 | flex-wrap: wrap; 13 | color: var(--color3); 14 | box-shadow: 10px 10px 0 var(--color5); 15 | 16 | &.FirstPost { 17 | flex-flow: column; 18 | max-height: 450px; 19 | padding: 10px; 20 | 21 | & .PostCover { 22 | width: 100%; 23 | height: auto; 24 | background-color: var(--color3); 25 | overflow: hidden; 26 | 27 | & img { 28 | width: 100%; 29 | height: 100%; 30 | object-fit: cover; 31 | } 32 | } 33 | } 34 | 35 | & .PostDetails { 36 | width: 100%; 37 | 38 | & h2 { 39 | color: var(--color2); 40 | margin: 3px 0; 41 | font-size: 30px; 42 | border-bottom: 1px solid var(--color1); 43 | 44 | &:hover, 45 | &:focus { 46 | border-bottom: 1px solid var(--color2); 47 | } 48 | } 49 | 50 | & p { 51 | margin: 5px 0; 52 | line-height: 1.5; 53 | } 54 | 55 | & .Extra { 56 | display: flex; 57 | align-items: center; 58 | flex-wrap: wrap; 59 | width: 100%; 60 | justify-content: space-between; 61 | 62 | & p { 63 | font-weight: bold; 64 | font-size: 14px; 65 | } 66 | 67 | & button { 68 | background: none; 69 | border: none; 70 | cursor: pointer; 71 | padding: 5px; 72 | 73 | &:hover, 74 | &:focus { 75 | text-decoration: underline; 76 | } 77 | 78 | &.Unsave { 79 | color: var(--color2); 80 | } 81 | 82 | &.Save { 83 | color: var(--color4); 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | @media only screen and (max-width: 800px) { 91 | .Post .PostDetails h2 { 92 | font-size: 20px; 93 | } 94 | } 95 | 96 | @media only screen and (max-width: 380px) { 97 | .Post .PostDetails .Extra > p { 98 | font-size: 11px; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/components/Blog/Styles/Posts.module.scss: -------------------------------------------------------------------------------- 1 | .LeftContents { 2 | color: var(--color3); 3 | justify-content: flex-end; 4 | border: 1px solid var(--color2); 5 | padding: 10px; 6 | border-radius: 5px; 7 | position: fixed; 8 | width: 200px; 9 | margin-bottom: 30px; 10 | z-index: 1; 11 | 12 | & > * { 13 | margin: 0; 14 | } 15 | } 16 | 17 | .TagsSection { 18 | width: 100%; 19 | padding: 10px; 20 | border: 1px solid var(--color2); 21 | border-radius: 5px; 22 | max-height: 500px; 23 | display: flex; 24 | flex-direction: column; 25 | overflow-y: auto; 26 | 27 | & h3 { 28 | color: var(--color2); 29 | margin: 3px 0; 30 | } 31 | 32 | & a { 33 | text-decoration: underline; 34 | margin: 1px 0; 35 | color: var(--color3); 36 | padding: 2px; 37 | 38 | &:hover, 39 | &:focus { 40 | outline: 1px solid var(--color2); 41 | color: var(--color2); 42 | text-decoration: none; 43 | } 44 | } 45 | } 46 | 47 | .PageHeader { 48 | display: none; 49 | width: 100%; 50 | margin: 10px auto 0; 51 | border: 1px solid var(--color1); 52 | color: var(--color3); 53 | flex-direction: column; 54 | justify-content: center; 55 | align-items: center; 56 | border-radius: 5px; 57 | 58 | & h2 { 59 | font-size: 30px; 60 | } 61 | 62 | & p { 63 | color: var(--color3); 64 | } 65 | 66 | & > * { 67 | margin: 4px 0; 68 | } 69 | } 70 | 71 | .Posts { 72 | width: 100%; 73 | margin: 0 auto; 74 | } 75 | 76 | .NoSavedPost { 77 | display: flex; 78 | align-items: center; 79 | 80 | & h1 { 81 | color: var(--color2); 82 | font-size: 25px; 83 | } 84 | } 85 | 86 | .Pagination { 87 | height: 50px; 88 | display: flex; 89 | justify-content: center; 90 | align-items: center; 91 | 92 | & > * { 93 | margin: 0 3px; 94 | } 95 | 96 | & > a { 97 | color: var(--color2); 98 | padding: 3px; 99 | 100 | &.ActiveLink { 101 | border-bottom: 1px solid var(--color2); 102 | } 103 | 104 | &:hover, 105 | &:focus { 106 | outline: 1px solid var(--color2); 107 | } 108 | } 109 | } 110 | 111 | @media only screen and (max-width: 800px) { 112 | .PageHeader { 113 | display: flex; 114 | } 115 | 116 | .Pagination a { 117 | font-size: 14px; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/components/Blog/SuggestArticles/SuggestArticles.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "./SuggestArticles.module.scss" 3 | 4 | export default () => ( 5 |
6 |

7 | Do you have any topic in web development which you would love to be 8 | written about? 9 |
10 | If yes, kindly{" "} 11 | 17 | create a new issue on the repository 18 | {" "} 19 | or{" "} 20 | 26 | fill this form 27 | 28 | . 29 |

30 |
31 | ) 32 | -------------------------------------------------------------------------------- /src/components/Blog/SuggestArticles/SuggestArticles.module.scss: -------------------------------------------------------------------------------- 1 | .Suggest { 2 | width: 100%; 3 | border: 1px solid var(--color5); 4 | border-left: 5px solid var(--color5); 5 | padding: 0 20px; 6 | margin: 20px 0; 7 | color: var(--color3); 8 | 9 | & a { 10 | color: var(--color2); 11 | padding: 3px; 12 | 13 | &:hover, 14 | &:focus { 15 | outline: 1px solid var(--color2); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/components/Blog/Tags/Tags.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import { StaticQuery, graphql, Link } from "gatsby" 4 | 5 | export default () => ( 6 | { 22 | const { edges } = data.allMarkdownRemark 23 | 24 | const tagsArr = [] 25 | const limit = 15 26 | 27 | edges.forEach((post) => { 28 | post.node.frontmatter.tags && 29 | post.node.frontmatter.tags.forEach((tag) => { 30 | if (tagsArr.length < limit && !tagsArr.includes(tag)) { 31 | tagsArr.push(tag) 32 | } 33 | }) 34 | }) 35 | 36 | return ( 37 | 38 | {tagsArr.map((tag, index) => ( 39 | 44 | {tag} 45 | 46 | ))} 47 | 48 | ) 49 | }} 50 | /> 51 | ) 52 | -------------------------------------------------------------------------------- /src/components/Blog/Templates/Post.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react" 2 | import Styles from "../Styles/Post.module.scss" 3 | 4 | import PropTypes from "prop-types" 5 | import { Link } from "gatsby" 6 | import { saveArticle, isArticleSaved, unSaveArticle } from "../Saved/Saved" 7 | 8 | const Post = (props) => { 9 | const postID = props.PostID 10 | const dontShowSaveButtons = props.dontShowSaveButtons 11 | 12 | const [isSaved, saveState] = useState(isArticleSaved(postID)) 13 | 14 | const unSave = (id) => { 15 | unSaveArticle(id) 16 | saveState(false) 17 | } 18 | 19 | const save = (id) => { 20 | if (saveArticle(id) === false) { 21 | saveState(false) 22 | } else { 23 | saveArticle(id) 24 | saveState(true) 25 | } 26 | } 27 | 28 | let PostPosition = props.PostPosition 29 | let FirstPostSpecialStyle = props.FirstPostSpecialStyle 30 | return ( 31 |
38 | {/* The first post has a different style 39 | 40 | // FirstPostSpecialStyle is applied conditionally*/} 41 | 42 | {/* The blog cover is only show for the first */} 43 | {props.CoverSource && FirstPostSpecialStyle && PostPosition === 0 ? ( 44 |
45 | {props.CoverAlt} 46 |
47 | ) : null} 48 |
49 | 50 |

{props.title}

51 | 52 |

53 | { 54 | // This ensures that the characters are limited to 150 characters 55 | props.excerpt.length > 150 56 | ? `${props.excerpt.substr(0, 150)}...` 57 | : props.excerpt 58 | } 59 |

60 |
61 |

{props.extraInfo}

62 | {!dontShowSaveButtons ? ( 63 | isSaved ? ( 64 | 74 | ) : ( 75 | 84 | ) 85 | ) : null} 86 |
87 |
88 |
89 | ) 90 | } 91 | 92 | Post.propTypes = { 93 | PostID: PropTypes.isRequired, 94 | PostPosition: PropTypes.number, 95 | FirstPostSpecialStyle: PropTypes.bool, 96 | CoverSource: PropTypes.string, 97 | CoverAlt: PropTypes.string, 98 | href: PropTypes.string.isRequired, 99 | title: PropTypes.string.isRequired, 100 | excerpt: PropTypes.string.isRequired, 101 | extraInfo: PropTypes.string.isRequired, 102 | } 103 | 104 | export default Post 105 | -------------------------------------------------------------------------------- /src/components/Blog/Templates/Posts.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "../Styles/Posts.module.scss" 3 | 4 | import Post from "./Post" 5 | import formatBlogDate from "../../../functions/dateFormatter" 6 | import { Link } from "gatsby" 7 | import Layout from "../../../containers/Layout/Layout" 8 | import Brand from "../../Brand/Details" 9 | import Tags from "../Tags/Tags" 10 | import Newsletter from "../../Newsletter/Newsletter" 11 | import SuggestArticles from "../SuggestArticles/SuggestArticles" 12 | import Not5YearOldLiterallyNote from "../../Not5YearOldLiterallyNote" 13 | 14 | export default ({ pageContext }) => { 15 | const { pageCount } = pageContext 16 | const { index: pageIndex } = pageContext 17 | const isFirst = pageContext.index === 1 18 | const isLast = pageContext.last 19 | const prevPage = !isFirst && pageIndex - 1 === 1 ? "/" : pageIndex - 1 20 | const nextPage = !isLast && pageContext.index + 1 21 | 22 | return ( 23 | 30 |

#tags

31 | 32 | 33 | } 34 | SecondSection={ 35 | <> 36 | 37 |
38 | {pageContext.group.map(({ node }, index) => { 39 | return ( 40 | 1 ? "s" : ""} read`} 55 | /> 56 | ) 57 | })} 58 |
59 | 60 | {/* Pagination */} 61 |
62 | {!isFirst && ( 63 | 64 | ← Previous 65 | 66 | )} 67 | {Array.from({ length: pageCount }, (_, i) => ( 68 | 73 | {i + 1} 74 | 75 | ))} 76 | {!isLast && ( 77 | 78 | Next → 79 | 80 | )} 81 |
82 | 83 | 84 | 85 | } 86 | /> 87 | ) 88 | } 89 | -------------------------------------------------------------------------------- /src/components/Blog/Views/CategoryPage.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "../Styles/Posts.module.scss" 3 | 4 | import { graphql } from "gatsby" 5 | import Layout from "../../../containers/Layout/Layout" 6 | import Brand from "../../Brand/Details" 7 | import Post from "../Templates/Post" 8 | import formatBlogDate from "../../../functions/dateFormatter" 9 | 10 | export default ({ pageContext, data }) => { 11 | const { category } = pageContext 12 | const { edges, totalCount } = data.allMarkdownRemark 13 | 14 | const CategoryHeader = ( 15 | 16 |

Category - {category.toUpperCase()}

17 |

18 | {totalCount} post{totalCount === 1 ? "" : "s"} published 19 |

20 |
21 | ) 22 | 23 | return ( 24 | {CategoryHeader}} 30 | SecondSection={ 31 | 32 |
{CategoryHeader}
33 |
34 | {edges.map(({ node }) => { 35 | const { excerpt, fields, frontmatter } = node 36 | const { slug } = fields 37 | const { title } = frontmatter 38 | 39 | return ( 40 | 1 ? "s" : ""} read`} 51 | /> 52 | ) 53 | })} 54 |
55 |
56 | } 57 | /> 58 | ) 59 | } 60 | 61 | export const categoryQuery = graphql` 62 | query($category: String) { 63 | allMarkdownRemark( 64 | sort: { fields: [frontmatter___date], order: DESC } 65 | filter: { frontmatter: { category: { in: [$category] } } } 66 | ) { 67 | totalCount 68 | edges { 69 | node { 70 | id 71 | fields { 72 | slug 73 | } 74 | frontmatter { 75 | title 76 | date 77 | } 78 | timeToRead 79 | excerpt 80 | } 81 | } 82 | } 83 | } 84 | ` 85 | -------------------------------------------------------------------------------- /src/components/Blog/Views/TagPage.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "../Styles/Posts.module.scss" 3 | 4 | import { graphql } from "gatsby" 5 | import Layout from "../../../containers/Layout/Layout" 6 | import Brand from "../../Brand/Details" 7 | import Post from "../Templates/Post" 8 | import formatBlogDate from "../../../functions/dateFormatter" 9 | 10 | export default ({ pageContext, data }) => { 11 | const { tag } = pageContext 12 | const { edges, totalCount } = data.allMarkdownRemark 13 | const tagHeader = ( 14 | 15 |

#{tag}

16 |

17 | {totalCount} post{totalCount === 1 ? "" : "s"} published 18 |

19 |
20 | ) 21 | 22 | return ( 23 | {tagHeader}} 29 | SecondSection={ 30 | 31 |
{tagHeader}
32 |
33 | {edges.map(({ node }) => { 34 | const { excerpt, fields, frontmatter } = node 35 | const { slug } = fields 36 | const { title } = frontmatter 37 | 38 | return ( 39 | 1 ? "s" : ""} read`} 50 | /> 51 | ) 52 | })} 53 |
54 |
55 | } 56 | /> 57 | ) 58 | } 59 | 60 | export const pageQuery = graphql` 61 | query($tag: String) { 62 | allMarkdownRemark( 63 | limit: 2000 64 | sort: { fields: [frontmatter___date], order: DESC } 65 | filter: { frontmatter: { tags: { in: [$tag] } } } 66 | ) { 67 | totalCount 68 | edges { 69 | node { 70 | id 71 | fields { 72 | slug 73 | } 74 | frontmatter { 75 | title 76 | date 77 | } 78 | timeToRead 79 | excerpt 80 | } 81 | } 82 | } 83 | } 84 | ` 85 | -------------------------------------------------------------------------------- /src/components/Brand/Details.js: -------------------------------------------------------------------------------- 1 | import Logo from "../../assets/img/logo.png" 2 | 3 | export default { 4 | name: "TheWebFor5", 5 | nameWithSpace: "The Web For 5", 6 | twitter: "thewebfor5", 7 | domain: "https://thewebfor5.com", 8 | repo: "https://github.com/dillionmegida/thewebfor5", 9 | logo: Logo, 10 | founder: { 11 | name: "Dillion Megida", 12 | twitter: "https://twitter.com/iamdillion", 13 | website: "https://dillionmegida.com", 14 | }, 15 | motto: "Simplifying web topics like you're five", 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "./Footer.module.scss" 3 | 4 | import Brand from "../Brand/Details" 5 | import { TwitterMedia } from "../SocialMedia/Media" 6 | 7 | export default () => { 8 | return ( 9 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.module.scss: -------------------------------------------------------------------------------- 1 | .Footer { 2 | height: 50px; 3 | background-color: var(--color1); 4 | border-top: 1px solid var(--color3); 5 | display: flex; 6 | flex-wrap: wrap; 7 | justify-content: center; 8 | align-items: center; 9 | padding: 10px 20px; 10 | color: var(--color3); 11 | 12 | & a { 13 | color: var(--color2); 14 | padding: 5px; 15 | 16 | &:hover, 17 | &:focus { 18 | outline: 1px solid var(--color2); 19 | } 20 | } 21 | 22 | & > * { 23 | margin: 1px; 24 | } 25 | 26 | & .Copyright { 27 | color: var(--color3); 28 | } 29 | 30 | & .SocialMedia { 31 | display: flex; 32 | } 33 | 34 | & .Repo { 35 | padding: 5px; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/components/Header/Header.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "./Header.module.scss" 3 | 4 | import { Link } from "gatsby" 5 | import Brand from "../Brand/Details" 6 | import NavLinks from "../Nav/NavLinks" 7 | 8 | export default (props) => { 9 | return ( 10 |
11 |
12 | 13 |
14 | {`${Brand.name} 15 |
16 |      17 | {Brand.name} 18 | 19 |
20 | 26 | 29 |
30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /src/components/Header/Header.module.scss: -------------------------------------------------------------------------------- 1 | .Header { 2 | background-color: var(--color1); 3 | height: 50px; 4 | width: 100%; 5 | display: flex; 6 | align-items: center; 7 | justify-content: space-between; 8 | border-bottom: 1px solid var(--color3); 9 | padding: 0 20px; 10 | position: fixed; 11 | top: 0; 12 | z-index: 1; 13 | 14 | & .Brand { 15 | & > a { 16 | color: var(--color2); 17 | height: 100%; 18 | display: flex; 19 | align-items: center; 20 | 21 | & .Logo { 22 | display: inline-block; 23 | width: 40px; 24 | height: 40px; 25 | 26 | & img { 27 | width: 100%; 28 | height: 100%; 29 | object-fit: cover; 30 | } 31 | } 32 | 33 | & .Name { 34 | font-size: 30px; 35 | } 36 | } 37 | } 38 | 39 | & .DrawerBtn { 40 | display: none; 41 | } 42 | } 43 | 44 | .NavLinks ul { 45 | display: flex; 46 | height: 100%; 47 | margin: 0; 48 | padding: 0; 49 | 50 | & .ActiveLink { 51 | font-weight: bold; 52 | color: var(--color2); 53 | background-color: var(--color2); 54 | color: var(--color1); 55 | } 56 | 57 | & li { 58 | margin: 0 5px; 59 | // width: 90px; 60 | width: 110px; 61 | height: 100%; 62 | 63 | & a { 64 | display: block; 65 | width: 100%; 66 | color: var(--color3); 67 | text-align: center; 68 | border-bottom: 1px solid var(--color1); 69 | padding: 5px; 70 | font-size: 15px; 71 | 72 | &:hover, 73 | &:focus { 74 | background-color: var(--color2); 75 | color: var(--color1); 76 | } 77 | } 78 | } 79 | } 80 | 81 | // @media only screen and (max-width: 670px) { 82 | // .NavLinks { 83 | // display: none; 84 | // } 85 | // } 86 | 87 | @media only screen and (max-width: 800px) { 88 | .Header ul li { 89 | width: 40px; 90 | font-size: 16px; 91 | } 92 | .Header .LinkTitleClass { 93 | display: none; 94 | } 95 | } 96 | 97 | @media only screen and (max-width: 670px) { 98 | .Header .Brand a .Name { 99 | font-size: 25px; 100 | } 101 | } 102 | 103 | @media only screen and (max-width: 500px) { 104 | .Header .Brand a .Name { 105 | display: none; 106 | } 107 | } 108 | 109 | // @media only screen and (max-width: 670px) { 110 | // .Header .DrawerBtn { 111 | // display: block; 112 | // } 113 | // } 114 | -------------------------------------------------------------------------------- /src/components/Helmet.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Helmet } from "react-helmet" 3 | import PropTypes from "prop-types" 4 | import Logo from "../assets/img/logo.png" 5 | import MainImg from "../assets/img/main.jpg" 6 | 7 | const AppHelmet = (props) => ( 8 | 9 | 10 | {props.PageTitle} 11 | {" "} 15 | {/* edit */} 16 | {/* edit */} 17 | {" "} 21 | {/* edit */} 22 | {/* Choose author name from blogs. If authorName prop isn't declared, use DIllion Megida */} 23 | 27 | 28 | 29 | 30 | 31 | 35 | 36 | 37 | 38 | 42 | 43 | {/* edit */} 44 | {" "} 45 | {/* edit */} 46 | {" "} 52 | {/* edit */} 53 | 57 | 58 | {/* Font awesome */} 59 | 60 | 61 | ) 62 | 63 | AppHelmet.propTypes = { 64 | PageTitle: PropTypes.string.isRequired, 65 | PageLink: PropTypes.string.isRequired, 66 | PageDescription: PropTypes.string.isRequired, 67 | PageKeywords: PropTypes.string.isRequired, 68 | SummaryCard: PropTypes.bool, 69 | } 70 | 71 | export default AppHelmet 72 | -------------------------------------------------------------------------------- /src/components/Nav/Drawer/Drawer.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "./Drawer.module.scss" 3 | 4 | import NavLinks from "../NavLinks" 5 | import Backdrop from "../../UI/Backdrop/Backdrop" 6 | 7 | export default (props) => ( 8 |
9 | 10 |
11 | 14 | 15 |
16 |
17 | ) 18 | -------------------------------------------------------------------------------- /src/components/Nav/Drawer/Drawer.module.scss: -------------------------------------------------------------------------------- 1 | .DrawerSection { 2 | display: none; 3 | margin: 0; 4 | } 5 | 6 | .Drawer { 7 | width: 70%; 8 | height: 100vh; 9 | left: 30%; 10 | top: 0; 11 | position: fixed; 12 | z-index: 5; 13 | background-color: var(--color2); 14 | 15 | & button { 16 | float: right; 17 | margin: 20px; 18 | background: none; 19 | color: red; 20 | border: 0; 21 | font-size: 30px; 22 | cursor: pointer; 23 | } 24 | 25 | & ul { 26 | width: 100%; 27 | display: flex; 28 | flex-direction: column; 29 | align-items: center; 30 | justify-content: center; 31 | padding-top: 50px; 32 | margin: 50px 0 0; 33 | padding: 0; 34 | 35 | & li { 36 | font-size: 20px; 37 | width: 90%; 38 | margin: 0 auto; 39 | display: flex; 40 | justify-content: center; 41 | 42 | & .ActiveLink { 43 | font-weight: bold; 44 | color: var(--color1); 45 | border-bottom: 1px solid var(--color1); 46 | } 47 | 48 | & a { 49 | width: 200px; 50 | display: block; 51 | color: var(--color1); 52 | text-align: center; 53 | padding: 5px; 54 | 55 | &:hover { 56 | color: var(--color1); 57 | border-bottom: 1px solid var(--color1); 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | @media only screen and (max-width: 670px) { 65 | .DrawerSection { 66 | display: block; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/components/Nav/NavLink.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Link } from "gatsby" 3 | 4 | export default (props) => ( 5 |
  • 6 | 11 | {/* The link title class is provided to remove the text (remaining the icon) on small screens */} 12 | {props.Icon}  {props.Nav} 13 | 14 |
  • 15 | ) 16 | -------------------------------------------------------------------------------- /src/components/Nav/NavLinks.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import NavLink from "./NavLink" 4 | import Brand from "../Brand/Details" 5 | 6 | const NavLists = [ 7 | { 8 | name: "Home", 9 | icon: "fa fa-home", 10 | href: "/", 11 | title: `${Brand.name} - Homepage`, 12 | }, 13 | { 14 | name: "Search", 15 | icon: "fa fa-search", 16 | href: "/search", 17 | title: `Search Topics`, 18 | }, 19 | { 20 | name: "Categories", 21 | icon: "fa fa-th-large", 22 | href: "/categories", 23 | title: `Web Development Categories`, 24 | }, 25 | { 26 | name: "About", 27 | icon: "fa fa-copyright", 28 | href: "/about", 29 | title: `About ${Brand.name}`, 30 | }, 31 | // , 32 | // { 33 | // // name: 'Contact', 34 | // name: 'fa fa-comments', 35 | // href: '/contact', 36 | // title: `Contact us at ${Brand.name}` 37 | // } 38 | ] 39 | 40 | export default (props) => ( 41 |
      42 | {NavLists.map((nav, index) => ( 43 | } 47 | Link={nav.href} 48 | Title={nav.title} 49 | LinkTitle={props.LinkTitleClass} 50 | ActiveClassName={props.ActiveClassName} 51 | /> 52 | ))} 53 |
    54 | ) 55 | -------------------------------------------------------------------------------- /src/components/Newsletter/Newsletter.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "./Newsletter.module.scss" 3 | 4 | export default () => ( 5 | 18 | ) 19 | -------------------------------------------------------------------------------- /src/components/Newsletter/Newsletter.module.scss: -------------------------------------------------------------------------------- 1 | .Newsletter { 2 | width: 230px; 3 | display: flex; 4 | justify-content: center; 5 | margin: 30px auto; 6 | 7 | & a { 8 | border: 1px solid var(--color5); 9 | padding: 5px; 10 | color: var(--color2); 11 | 12 | &:hover, 13 | &:focus { 14 | border: 1px solid var(--color3); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Not5YearOldLiterallyNote/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | import React from "react" 3 | 4 | const Container = styled.div` 5 | background-color: var(--color1); 6 | color: var(--color3); 7 | padding: 20px; 8 | border-radius: 5px; 9 | border: 1px solid var(--color2); 10 | h1 { 11 | font-weight: bold; 12 | margin: 0; 13 | font-size: 18px; 14 | display: block; 15 | margin-bottom: 10px; 16 | position: relative; 17 | overflow: hidden; 18 | &::after { 19 | content: ""; 20 | height: 1px; 21 | background-color: var(--color5); 22 | width: 100%; 23 | position: absolute; 24 | left: 0; 25 | top: 0; 26 | margin-top: 13px; 27 | margin-left: 100px; 28 | } 29 | } 30 | p { 31 | margin: 0; 32 | } 33 | ` 34 | 35 | export default function Not5YearOldLiterallyNote() { 36 | return ( 37 | 38 |

    Welcome

    39 |

    40 | We do not expect an actual 5 year old on this platform. The term 41 | "WebFor5" connotes simplicity in our articles. We ensure that our 42 | articles on various web development topics are easy to understand and 43 | digest 😊 44 |

    45 |
    46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /src/components/SocialMedia/Media.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Twitter, Web } from "../UI/Icons" 3 | 4 | const TwitterMedia = (props) => ( 5 | 6 | 7 | 8 | ) 9 | 10 | const WebsiteMedia = (props) => ( 11 | 12 | 13 | 14 | ) 15 | 16 | export { TwitterMedia, WebsiteMedia } 17 | -------------------------------------------------------------------------------- /src/components/SocialMedia/ShareArticle.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "./ShareArticle.module.scss" 3 | 4 | import { Twitter, Share } from "../UI/Icons" 5 | import Brand from "../Brand/Details" 6 | import { checkGlobal } from "../Blog/Saved/Saved" 7 | 8 | const checkNativeShare = () => checkGlobal() && navigator.share 9 | 10 | const sharedText = (url, title, author) => 11 | `${title} by @${author} on @${Brand.twitter} - ${ 12 | Brand.domain 13 | }${url} %23${Brand.name.toLowerCase()}` 14 | 15 | const nativeShare = (url, title, author) => { 16 | navigator 17 | .share({ 18 | title, 19 | url, 20 | text: `${title} by @${author} on @${ 21 | Brand.twitter 22 | } #${Brand.name.toLowerCase()}`, 23 | }) 24 | .then(() => { 25 | console.log("Successful shared article.") 26 | }) 27 | .catch((err) => console.log("Couldn't share articel because ", err)) 28 | } 29 | 30 | const ShareTwitter = (props) => { 31 | const text = props.text 32 | return ( 33 | 39 | 40 | 41 | ) 42 | } 43 | 44 | const OtherShare = (props) => { 45 | const url = props.url 46 | const title = props.title 47 | const author = props.author 48 | return ( 49 | <> 50 | 51 | 52 | ) 53 | } 54 | 55 | const ShareArticle = (props) => { 56 | const url = props.url 57 | const title = props.title 58 | const author = props.author 59 | 60 | return ( 61 | 62 |
    63 |

    64 | Kindly share this article{" "} 65 | 66 | 😃 67 | 68 |

    69 |
    70 | {checkNativeShare() !== undefined ? ( 71 | 77 | ) : ( 78 | 79 | )} 80 |
    81 |
    82 |
    83 | ) 84 | } 85 | 86 | export default ShareArticle 87 | -------------------------------------------------------------------------------- /src/components/SocialMedia/ShareArticle.module.scss: -------------------------------------------------------------------------------- 1 | .Link { 2 | font-size: 20px; 3 | 4 | & i { 5 | color: #1da1f2; 6 | } 7 | } 8 | 9 | .ShareArticle { 10 | margin: 10px 0 0; 11 | border-top: 1px solid var(--color5); 12 | padding: 10px; 13 | 14 | & p { 15 | font-size: 20px; 16 | margin: 5px 0; 17 | color: var(--color3); 18 | } 19 | 20 | & .Methods { 21 | display: flex; 22 | align-items: center; 23 | height: 60px; 24 | 25 | & > * { 26 | margin: 0 10px; 27 | } 28 | 29 | & button { 30 | background: var(--color2); 31 | padding: 5px 10px; 32 | border: none; 33 | cursor: pointer; 34 | color: var(--color1); 35 | border-radius: 5px; 36 | 37 | &:hover, 38 | &:focus { 39 | outline: 1px solid var(--color2); 40 | background: var(--color1); 41 | color: var(--color2); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/components/UI/Backdrop/Backdrop.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "./Backdrop.module.scss" 3 | 4 | export default () =>
    5 | -------------------------------------------------------------------------------- /src/components/UI/Backdrop/Backdrop.module.scss: -------------------------------------------------------------------------------- 1 | .Backdrop { 2 | position: fixed; 3 | top: 80px; 4 | width: 100%; 5 | height: calc(100% - 80px); 6 | background-color: rgba(0, 0, 0, 0.6); 7 | } 8 | -------------------------------------------------------------------------------- /src/components/UI/Icons.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react" 2 | 3 | const Icon = (props) => ( 4 | 5 | 6 | 7 | ) 8 | 9 | const BackArrow = () => 10 | 11 | const Web = () => 12 | 13 | const Share = () => 14 | 15 | // Theme 16 | const Sun = () => 17 | const Moon = () => 18 | 19 | // Social Media Icons 20 | const Twitter = () => 21 | 22 | export { Share, BackArrow, Web, Twitter, Sun, Moon } 23 | -------------------------------------------------------------------------------- /src/components/UI/Theme/Theme.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "./Theme.module.scss" 3 | 4 | import Brand from "../../Brand/Details" 5 | import { checkGlobal } from "../../Blog/Saved/Saved" 6 | import { Sun, Moon } from "../Icons" 7 | 8 | const storageKey = `${Brand.name}_Theme` 9 | 10 | // initialize theme not working for some reasons 11 | // but the site is working fine. I would be expecting issue reports 12 | // if(checkGlobal()) { 13 | // let prevTheme = window.localStorage.getItem(storageKey); 14 | // if(!prevTheme === null) { 15 | // window.localStorage.setItem(storageKey, 'light'); 16 | // } 17 | // } 18 | 19 | const saveDark = () => { 20 | if (checkGlobal()) { 21 | window.localStorage.setItem(storageKey, "dark") 22 | } 23 | } 24 | 25 | const saveLight = () => { 26 | if (checkGlobal()) { 27 | window.localStorage.setItem(storageKey, "light") 28 | } 29 | } 30 | 31 | const isThemeLight = () => { 32 | if (checkGlobal()) { 33 | let currentTheme = window.localStorage.getItem(storageKey) 34 | return currentTheme === "light" 35 | } 36 | } 37 | 38 | export default (props) => { 39 | return ( 40 | 41 | {isThemeLight() ? ( 42 | 45 | ) : ( 46 | 49 | )} 50 | 51 | ) 52 | } 53 | 54 | export { saveDark, saveLight, isThemeLight } 55 | -------------------------------------------------------------------------------- /src/components/UI/Theme/Theme.module.scss: -------------------------------------------------------------------------------- 1 | .DarkBtn, 2 | .LightBtn { 3 | background: none; 4 | border: none; 5 | cursor: pointer; 6 | padding: 5px; 7 | font-size: 10px; 8 | width: 25px; 9 | 10 | & i { 11 | font-size: 25px; 12 | } 13 | 14 | &:focus { 15 | outline: 1px solid var(--color1); 16 | } 17 | } 18 | 19 | .DarkBtn i { 20 | // display: none; 21 | color: lightgreen; 22 | text-shadow: 1px 0px 3px lightgreen; 23 | } 24 | 25 | .LightBtn i { 26 | color: orange; 27 | text-shadow: 1px 0px 3px orange; 28 | } 29 | -------------------------------------------------------------------------------- /src/containers/Layout/Layout.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "./Layout.module.scss" 3 | 4 | import { Link } from "gatsby" 5 | import Helmet from "../../components/Helmet" 6 | import Header from "../../components/Header/Header" 7 | import Footer from "../../components/Footer/Footer" 8 | import Theme, { 9 | saveDark, 10 | saveLight, 11 | isThemeLight, 12 | } from "../../components/UI/Theme/Theme" 13 | 14 | export default class Layout extends React.Component { 15 | state = { 16 | lightTheme: isThemeLight(), 17 | } 18 | 19 | changeTheme = () => { 20 | if (this.state.lightTheme === false) { 21 | saveLight() 22 | this.setState({ 23 | lightTheme: true, 24 | }) 25 | } else { 26 | saveDark() 27 | this.setState({ 28 | lightTheme: false, 29 | }) 30 | } 31 | } 32 | 33 | render() { 34 | return ( 35 |
    42 | 50 | 51 |
    52 | 53 |
    54 | 60 | 61 | 62 | 63 |
    64 | 65 |
    66 | {this.props.children} 67 | 68 | { 69 | /* This checks if first section is stated so it doesn't add to the DOM */ 70 | this.props.FirstSection ? ( 71 |
    72 | {this.props.FirstSection} 73 |
    74 | ) : null 75 | } 76 | 77 | { 78 | /* This checks if second section is stated so it doesn't add to the DOM */ 79 | this.props.SecondSection ? ( 80 |
    81 | {this.props.SecondSection} 82 |
    83 | ) : null 84 | } 85 |
    86 |
    87 |
    88 | ) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/containers/Layout/Layout.module.scss: -------------------------------------------------------------------------------- 1 | .Layout { 2 | /* Themes */ 3 | /* color1 - header, page and footer background color 4 | color2 - navlinks and headers 5 | color 3 - text color 6 | color 4 - bloglinks 7 | color 5 - boxshadow 8 | */ 9 | --color1: white; 10 | --color2: #a52a2a; 11 | --color3: #333; 12 | --color4: green; 13 | --color5: #ddd; 14 | // background-color: var(--color1); 15 | } 16 | 17 | .Layout.Dark { 18 | -webkit-font-smoothing: antialiased; 19 | --color1: #262625; 20 | --color2: #90ee90; 21 | --color3: white; 22 | --color4: #8ce21c; 23 | --color5: #234850; 24 | } 25 | 26 | .Layout { 27 | transition: color 0.1s ease-out, background 0.1s ease-out; 28 | background-color: var(--color1); 29 | 30 | & .OtherMenus { 31 | position: relative; 32 | background-color: var(--color1); 33 | width: 100%; 34 | min-height: 50px; 35 | padding: 0 20px; 36 | top: 50px; 37 | display: flex; 38 | justify-content: flex-end; 39 | align-items: center; 40 | z-index: 0; 41 | 42 | & > * { 43 | margin: 0 10px; 44 | } 45 | 46 | & > .Saved { 47 | font-size: 25px; 48 | color: var(--color4); 49 | 50 | &:hover, 51 | & .SavedActive { 52 | color: var(--color2); 53 | } 54 | } 55 | } 56 | 57 | & .BodyContent { 58 | width: 95%; 59 | margin: 50px auto 20px; 60 | display: flex; 61 | flex-wrap: wrap; 62 | justify-content: space-around; 63 | /* calculated to be the height of the page */ 64 | min-height: calc(100vh - 70px - 50px - 50px); 65 | 66 | & .FirstSection { 67 | width: 200px; 68 | max-height: 100vh; 69 | margin: 40px 10px; 70 | display: flex; 71 | flex-direction: column; 72 | overflow-y: auto; 73 | } 74 | 75 | & .SecondSection { 76 | width: 60%; 77 | margin: 20px auto 0; 78 | } 79 | } 80 | } 81 | 82 | @media only screen and (max-width: 800px) { 83 | .Layout .BodyContent .FirstSection { 84 | display: none; 85 | } 86 | .Layout .BodyContent .SecondSection { 87 | width: 90%; 88 | } 89 | // .Layout .BodyContent .ThirdSection { 90 | // display: none; 91 | // } 92 | } 93 | -------------------------------------------------------------------------------- /src/containers/Search/Search.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "./Search.module.scss" 3 | 4 | import { StaticQuery, graphql } from "gatsby" 5 | import Post from "../../components/Blog/Templates/Post" 6 | import formatBlogDate from "../../functions/dateFormatter" 7 | 8 | export default class Search extends React.Component { 9 | state = { 10 | filteredArr: [], 11 | } 12 | 13 | handleInput = (wholeArr, event) => { 14 | const query = event.target.value.toLowerCase() 15 | 16 | // check if the query is blank because the every post will pass the filter test for an empty input 17 | if (query === "") { 18 | this.setState({ 19 | filteredArr: [], 20 | }) 21 | return 22 | } 23 | const posts = wholeArr.edges 24 | const filteredArr = posts.filter((post) => { 25 | const { pageDescription, title, tags } = post.node.frontmatter 26 | 27 | console.log(pageDescription, title, tags) 28 | 29 | return ( 30 | pageDescription.toLowerCase().includes(query) || 31 | title.toLowerCase().includes(query) || 32 | tags.join("").toLowerCase().includes(query) 33 | ) 34 | }) 35 | this.setState({ 36 | filteredArr: filteredArr, 37 | }) 38 | } 39 | 40 | render() { 41 | return ( 42 | { 68 | const { allPosts } = data 69 | const { totalCount: noOfArticles } = allPosts 70 | const arrLength = this.state.filteredArr.length 71 | 72 | return ( 73 | 74 |

    80 | {noOfArticles} articles published 81 |

    82 |
    83 | { 87 | this.handleInput(allPosts, event) 88 | }} 89 | /> 90 |

    {arrLength}

    91 |
    92 | {arrLength !== 0 ? ( 93 |
    94 | {this.state.filteredArr.map(({ node }) => ( 95 |
    96 | 1 ? "s" : "" 106 | } read`} 107 | /> 108 |
    109 | ))} 110 |
    111 | ) : null} 112 |
    113 | ) 114 | }} 115 | /> 116 | ) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/containers/Search/Search.module.scss: -------------------------------------------------------------------------------- 1 | .Search { 2 | width: 100%; 3 | display: flex; 4 | padding: 0 10px 0 0; 5 | display: flex; 6 | align-items: center; 7 | 8 | & input { 9 | width: 95%; 10 | border: 1px solid #ddd; 11 | font-size: 16px; 12 | background-color: var(--color1); 13 | color: var(--color3); 14 | padding: 10px 0 10px 10px; 15 | 16 | &:hover, 17 | &:focus { 18 | outline: none; 19 | background-color: var(--color5); 20 | } 21 | } 22 | 23 | & p { 24 | width: 5%; 25 | margin: 0; 26 | text-align: center; 27 | color: var(--color3); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/functions/dateFormatter.js: -------------------------------------------------------------------------------- 1 | // This function converts dates like 2001-01-13 to 13th January, 2001 2 | // I guess there's a better way to do it in GatsbyJS, but I haven't figured out yet. 3 | 4 | const formatBlogDate = (date) => { 5 | let dateArr = date.split("") 6 | 7 | // Slice the array to break the date 8 | let yearArr = dateArr.slice(0, 4) 9 | let monthArr = dateArr.slice(5, 7) 10 | let dayArr = dateArr.slice(8, 10) 11 | 12 | // parse the dates to integers and also get the last digit of the day for day suffixes 13 | let month = parseInt(monthArr.join("")) 14 | let day = parseInt(dayArr.join("")) 15 | let lastDigitOfDay = parseInt( 16 | dayArr.join("").charAt(dayArr.join("").length - 1) 17 | ) 18 | let year = parseInt(yearArr.join("")) 19 | 20 | // switch case to get the month name 21 | let monthName 22 | 23 | switch (month) { 24 | case 1: 25 | monthName = "January" 26 | break 27 | case 2: 28 | monthName = "February" 29 | break 30 | case 3: 31 | monthName = "March" 32 | break 33 | case 4: 34 | monthName = "April" 35 | break 36 | case 5: 37 | monthName = "May" 38 | break 39 | case 6: 40 | monthName = "June" 41 | break 42 | case 7: 43 | monthName = "July" 44 | break 45 | case 8: 46 | monthName = "August" 47 | break 48 | case 9: 49 | monthName = "September" 50 | break 51 | case 10: 52 | monthName = "October" 53 | break 54 | case 11: 55 | monthName = "November" 56 | break 57 | case 12: 58 | monthName = "December" 59 | break 60 | default: 61 | monthName = "" 62 | } 63 | 64 | // switch case to get the suffix of the day 65 | let daySuffix 66 | 67 | switch (lastDigitOfDay) { 68 | case 1: 69 | daySuffix = "st" 70 | break 71 | case 2: 72 | daySuffix = "nd" 73 | break 74 | case 3: 75 | daySuffix = "rd" 76 | break 77 | default: 78 | daySuffix = "th" 79 | } 80 | 81 | // exceptions for 11 to 19 82 | switch (day) { 83 | case 11: 84 | case 12: 85 | case 13: 86 | daySuffix = "th" 87 | break 88 | default: 89 | break 90 | } 91 | 92 | let fullDate = `${monthName} ${day}${daySuffix}, ${year}` 93 | return fullDate 94 | } 95 | 96 | export default formatBlogDate 97 | -------------------------------------------------------------------------------- /src/hooks/allCategories.js: -------------------------------------------------------------------------------- 1 | import { useStaticQuery, graphql } from "gatsby" 2 | 3 | export default () => { 4 | const result = useStaticQuery(graphql` 5 | query { 6 | categories: allMarkdownRemark { 7 | edges { 8 | node { 9 | id 10 | frontmatter { 11 | category 12 | } 13 | } 14 | } 15 | } 16 | } 17 | `) 18 | 19 | const { edges } = result.categories 20 | 21 | // To get Post Counts 22 | 23 | let GeneralArr, HTMLArr, CSSArr, JavascriptArr, GitArr 24 | 25 | GeneralArr = edges.filter( 26 | ({ node }) => node.frontmatter.category === "general" 27 | ) 28 | 29 | HTMLArr = edges.filter(({ node }) => node.frontmatter.category === "html") 30 | 31 | CSSArr = edges.filter(({ node }) => node.frontmatter.category === "css") 32 | 33 | JavascriptArr = edges.filter( 34 | ({ node }) => node.frontmatter.category === "javascript" 35 | ) 36 | 37 | GitArr = edges.filter(({ node }) => node.frontmatter.category === "git") 38 | 39 | const NumOfPosts = { 40 | General: GeneralArr.length, 41 | HTML: HTMLArr.length, 42 | CSS: CSSArr.length, 43 | Javascript: JavascriptArr.length, 44 | Git: GitArr.length, 45 | } 46 | 47 | return [ 48 | { 49 | name: "CSS", 50 | excerpt: "Cascading Stylesheet", 51 | link: "/css", 52 | numOfPosts: NumOfPosts.CSS, 53 | }, 54 | { 55 | name: "General", 56 | excerpt: "General web topics", 57 | link: "/general", 58 | numOfPosts: NumOfPosts.General, 59 | }, 60 | { 61 | name: "Git", 62 | excerpt: "Distributed version control", 63 | link: "/git", 64 | numOfPosts: NumOfPosts.Git, 65 | }, 66 | { 67 | name: "HTML", 68 | excerpt: "HyperText Markup Language", 69 | link: "/html", 70 | numOfPosts: NumOfPosts.HTML, 71 | }, 72 | { 73 | name: "Javascript", 74 | excerpt: "Scripting language for the web", 75 | link: "/javascript", 76 | numOfPosts: NumOfPosts.Javascript, 77 | }, 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "../styles/404.module.scss" 3 | 4 | import Layout from "../containers/Layout/Layout" 5 | import { Link } from "gatsby" 6 | import ErrorImg from "../assets/img/404.png" 7 | import { BackArrow } from "../components/UI/Icons" 8 | 9 | export default () => { 10 | const Header = () => ( 11 | <> 12 |

    404!

    13 |

    Page Not Found!!

    14 | 15 | ) 16 | 17 | const HomepageLink = () => ( 18 | 19 | Go To Homepage 20 | 21 | ) 22 | 23 | return ( 24 | 27 |
    28 | 29 | 30 | } 31 | SecondSection={ 32 |
    33 |
    34 |
    35 |
    36 |
    37 | A boy with a map looking for his way 38 |
    39 | 40 |
    41 | } 42 | /> 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /src/pages/about.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "../styles/about.module.scss" 3 | 4 | import { graphql } from "gatsby" 5 | import Layout from "../containers/Layout/Layout" 6 | import Brand from "../components/Brand/Details" 7 | import Founder from "../assets/img/dillion.jpg" 8 | import { TwitterMedia, WebsiteMedia } from "../components/SocialMedia/Media" 9 | import SuggestArticles from "../components/Blog/SuggestArticles/SuggestArticles" 10 | 11 | export default ({ data }) => { 12 | const { totalCount: noOfArticles } = data.allMarkdownRemark 13 | 14 | return ( 15 | 21 |
    22 |
    23 |

    {Brand.name}

    24 |

    {Brand.motto}

    25 |
    26 |
    27 |

    About

    28 |

    29 | {Brand.name} is a blog platform for demystifying web topics and 30 | making them easy to understand and digest. The term "Web For 5" can 31 | be confusing, but this platform is not targetted towards 5 year 32 | olds. The term connotes simplicity, which means we do our best on 33 | this platform to make short and long content simple. 34 |
    35 |
    36 | It was launched by {Brand.founder.name} on January 1st, 2020 (more 37 | details on him below). 38 |

    39 |

    40 | So far, there are {noOfArticles} articles written. 41 |

    42 |

    43 | We hope to give you the best in this platform : ). Kindly reach us 44 | on twitter -{" "} 45 | @{Brand.name}{" "} 46 | for any contributions or questions. 47 |

    48 |
    49 | 50 |
    51 |
    52 | {`${Brand.founder.name}, 56 |
    57 |
    58 |

    59 | {Brand.founder.name} 60 |

    61 |

    62 | A software engineer and technical writer. He founded this site 63 | with the purpose of making web topics and practices easier to 64 | understand. 65 |

    66 |
    67 | {" "} 71 |   72 | 76 |
    77 |
    78 |
    79 | 80 | 81 | 82 |
    83 |

    About This Site

    84 |

    85 | This site is built with{" "} 86 | 87 | GatsbyJS 88 | {" "} 89 | and hosted on{" "} 90 | 91 | Netlify 92 | 93 | . With this{" "} 94 | 98 | tutorial on GatsbyJS 99 | 100 | , you would get to understand how to work with the framework if you 101 | have no prior understanding. 102 |

    103 |

    104 | It is also open sourced on GitHub and contributions are welcome. 105 |

    106 | 111 | Contribute on GitHub 112 | 113 |
    114 |
    115 |
    116 | ) 117 | } 118 | 119 | export const query = graphql` 120 | query { 121 | allMarkdownRemark { 122 | totalCount 123 | } 124 | } 125 | ` 126 | -------------------------------------------------------------------------------- /src/pages/author/dillionmegida.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import Author from "../../components/Author/Author" 4 | 5 | export default () => { 6 | return 7 | } 8 | -------------------------------------------------------------------------------- /src/pages/author/ejidikeesther.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import Author from "../../components/Author/Author" 4 | 5 | export default () => { 6 | return 7 | } 8 | -------------------------------------------------------------------------------- /src/pages/categories.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Styles from "../styles/categories.module.scss" 3 | import Styles2 from "../styles/index.module.scss" 4 | 5 | import { Link, graphql } from "gatsby" 6 | import Layout from "../containers/Layout/Layout" 7 | import Tags from "../components/Blog/Tags/Tags" 8 | import Brand from "../components/Brand/Details" 9 | import allCategories from "../hooks/allCategories" 10 | 11 | export default ({ data }) => { 12 | const categories = allCategories() 13 | 14 | return ( 15 | 16 | category.name 22 | )}`} 23 | FirstSection={ 24 |
    25 |

    #tags

    26 | 27 |
    28 | } 29 | SecondSection={ 30 |
    31 |

    TOPIC CATEGORIES

    32 |
    33 | {categories.map((category, index) => ( 34 | 39 |

    {category.name}

    40 |
    41 |

    {category.excerpt}

    42 |

    43 | {category.numOfPosts} post 44 | {category.numOfPosts === 1 ? "" : "s"} 45 |

    46 |
    47 | 48 | ))} 49 |
    50 |
    51 | } 52 | /> 53 |
    54 | ) 55 | } 56 | 57 | export const query = graphql` 58 | query { 59 | AllPosts: allMarkdownRemark { 60 | edges { 61 | node { 62 | id 63 | frontmatter { 64 | category 65 | } 66 | } 67 | } 68 | } 69 | } 70 | ` 71 | -------------------------------------------------------------------------------- /src/pages/p/css/column-count/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dillionmegida/thewebfor5/214a67edffb2c4d2a5d26b686d5e10173274f561/src/pages/p/css/column-count/example.png -------------------------------------------------------------------------------- /src/pages/p/css/column-count/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: column-count in CSS, For Responsive Layouts 3 | category: css 4 | date: 2020-04-24 5 | authorID: 1 6 | pageDescription: >- 7 | column-count is a CSS property used on elements to split their immediate children to specified columns. The browser helps in trying to give allocate enough or required spaces for the children. 8 | pageKeywords: "css, column-gap, column-count, mansory, layouts, responsive layouts, amazing layouts, mansory layouts" 9 | cover: "https://res.cloudinary.com/dillionmegida/image/upload/v1587696830/images/thewebfor5/column-count_frpocd.png" 10 | 11 | tags: ["css", "layout"] 12 | --- 13 | 14 | Whenever we think of responsive or amazing layouts, grids of flexbox are our first options. `float`s `inline-blocks` and so on are slowly fading in its usage for layouts. But mind me say that, responsiveness is not all about grids and flexbox. Yes, they make complex layouts achievable, do the hardwork for us, **perfectly center align our elements (justify-content and align-items to the rescue 😍)** and they are amazing abstractions, but CSS offers a lot of simple-to-use tools which you might also consider. 15 | 16 | --- 17 | 18 | ## `column-count` 19 | 20 | `column-count` is one powerful, simple-to-use and not so popular tool that helps achieve some of our design desires. Talk about **responsiveness** as a piece of cake. 21 | 22 | This property allows us to specify the number of columns a container would have and the browser will try to allocate spaces for the elements while maintaining the three columns. 23 | 24 | It accepts four values: 25 | 26 | - `integer`: a positve integer greater than or equal to 0 used to specify the number of columns. 27 | - `auto`: which is default value specifies the number of columns based on other properties. 28 | - `inherit`: specifies that the property should be inherited from the parent element. 29 | - `initial`: set to the default value. 30 | 31 | We'll be maximizing the benefits of the positive integer in this article. Let's look at some examples: 32 | 33 | ### Example 1 34 | 35 | Let's look at a quick example: 36 | 37 | _HTML file_ 38 | 39 | ```html 40 |
    41 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc lectus massa, 42 | sagittis quis urna euismod, vulputate ornare turpis. In iaculis, lorem nec 43 | maximus fermentum, libero mauris egestas risus, vitae tincidunt orci felis at 44 | arcu. Maecenas sit amet rutrum nunc, id pulvinar sem. Sed et condimentum 45 | turpis. Phasellus finibus facilisis orci id vehicula. Duis pharetra odio et 46 | ipsum ornare, ac suscipit lectus suscipit. Fusce vestibulum dui sed augue 47 | finibus, ut imperdiet ligula lacinia. Cras at molestie ex. Duis tempus turpis 48 | ex, ut eleifend arcu vestibulum ac. 49 |
    50 | ``` 51 | 52 | _CSS file_ 53 | 54 | ```css 55 | .container { 56 | column-count: 3; 57 | } 58 | ``` 59 | 60 | ![column-count example](./example.png) 61 | 62 | As seen in the above image, the text is splitted into three columns by the browser. This gives us a starting point of the amazing layouts that can be created. Let's look at another example with some other properties that works with `column-count`. 63 | 64 | ### Example 2 65 | 66 | ```html 67 |
    68 |
    69 |
    70 |
    71 |
    72 |
    73 |
    74 |
    75 |
    76 |
    77 |
    78 |
    79 |
    80 |
    81 |
    82 |
    83 |
    84 |
    85 |
    86 |
    87 |
    88 |
    89 |
    90 | ``` 91 | 92 | ```css 93 | .container { 94 | column-count: 3; 95 | column-gap: 30px; 96 | column-rule: 1px solid purple; 97 | width: 600px; 98 | margin: 0 auto; 99 | } 100 | 101 | .elem { 102 | display: inline-block; 103 | margin-bottom: 30px; 104 | height: 100px; 105 | background-color: purple; 106 | width: 100%; 107 | } 108 | 109 | .elem:nth-child(1), 110 | .elem:nth-child(2), 111 | .elem:nth-child(5), 112 | .elem:nth-child(6) { 113 | margin: 0 auto initial; 114 | } 115 | 116 | .elem:nth-child(5) { 117 | height: 140px; 118 | } 119 | 120 | .elem:nth-child(2) { 121 | height: 200px; 122 | } 123 | 124 | .elem div { 125 | height: 60px; 126 | background-color: yellowgreen; 127 | } 128 | ``` 129 | 130 | {% codepen https://codepen.io/Dillion/pen/LYpWYbZ %} 131 | 132 | ### Other properties used 133 | 134 | - `column-gap`: this specifies the spaces between the columns. 135 | - `column-rule`: is a combination of `column-rule-width` (the rule between between the columns), `column-rule-style` (the style of the rule) and `column-rule-color` (the color of the rule). 136 | 137 | If `inline-block` is not used on the divs, the browser might allow an element to overflow to the next column. You can try it out on the codepen (by clicking the edit option) and removing the inline-block style. 138 | 139 | ## How about responsiveness? 140 | 141 | `column-count` accepts numbers which defines the number of columns as we have seen. With media queries, we can change that number at specific media screen widths. For example: 142 | 143 | ```css 144 | @media only screen and (max-width: 500px) { 145 | .container { 146 | column-count: 2; 147 | width: 100%; 148 | } 149 | } 150 | @media only screen and (max-width: 300px) { 151 | .container { 152 | column-count: 1; 153 | } 154 | } 155 | ``` 156 | 157 | You can add these media rules to the css space of the codepen above (when you click the edit option) and try resizing the result space or your browser width to see the columns change. 158 | 159 | ## Wrap up 160 | 161 | I got to discover this tool, while trying to achieve a [mansory layout of blocks](https://www.sitepoint.com/understanding-masonry-layout/) with flexbox. It was difficult because, in a flex container, all elements in a row will have to start from the same top base. So if we had three columns, and the first element in the first row had a lower height than the remaining two on the row, instead of the fourth element to be closer to the first, it will start from the same base which the fifth and six would also. 162 | 163 | `flex` and `grid` are awesome tools for layouts, but simple tools like `column-count` are also powerful in their own ways. 164 | 165 | Thanks for reading 💛 166 | -------------------------------------------------------------------------------- /src/pages/p/css/css-selector-methods.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Selector Methods in CSS 3 | category: css 4 | date: 2019-12-29 5 | authorID: 1 6 | pageDescription: >- 7 | There a numerous methods for selecting elements which CSS styles can be applied to. In this article, we'd be looking at some of them. 8 | pageKeywords: "css, css selector methods, by class, by id" 9 | cover: 10 | tags: ["css"] 11 | --- 12 | 13 | ```css 14 | p { 15 | color: red; 16 | font-size: 90px; 17 | } 18 | ``` 19 | 20 | In the above code, all `p` tags are selected will be affected by the style declarations. On most occassions, this is not our intention. Sometimes, a developer may just want to select some paragraphs or some images to style. In this article, we'd look at some methods to succintly select elements. 21 | 22 | ## Methods for selecting elements with CSS 23 | 24 | Five methods we'd be looking at are: 25 | 26 | - Parent and children 27 | - By class method 28 | - By id method 29 | - Universal selector 30 | - Attribute Method 31 | 32 | ### 1. Parent and Children Method 33 | 34 | Just as tags are related in HTML, you can target elements. 35 | 36 | ```css 37 | div p { 38 | color: pink; 39 | font-size: 20px; 40 | } 41 | ``` 42 | 43 | The above code doesn't style only `div` elements nor only `p` elements. It applies the declared styles to `p` elements which are children of `div`. 44 | 45 | ```css 46 | main div p { 47 | color: brown; 48 | font-size: 30px; 49 | } 50 | ``` 51 | 52 | As you may have guessed, the above code applies the declared styles to `p` elements which are grandchildren of `main` 53 | 54 | ### 2. Class method 55 | 56 | =Almost all elements in HTML can have a `class` attribute. This allows us to group related elements to the same class. With this class, we can select specific elements to style. 57 | 58 | ```html 59 | ... 60 |

    I am only red

    61 |

    I am only big

    62 |

    I am red and big

    63 | ... 64 | ``` 65 | 66 | The above is the definition on the HTML file. The first two elements has only one class each, but the third as two classes. Let's head over to css. 67 | 68 | ```css 69 | red { 70 | color: red; 71 | } 72 | ``` 73 | 74 | The above is **completely wrong!**. Why? Because, when you specify `red`, CSS would think it's a tagname and this doesn't exist. To select classes in CSS, the **dot (.)** prefix is used. 75 | 76 | ```css 77 | .red { 78 | color: red; 79 | } 80 | .big { 81 | font-size: 9000px; 82 | } 83 | ``` 84 | 85 | This means that our first paragraph would be red colored and the default font size, the second would be font size of 9000px but the default color remains. While the last paragraph would be red colored and of font size 9000px. 86 | 87 | With this, other paragraphs without these classes are not affected in any way. 88 | 89 | ### 3. ID method 90 | 91 | Similar to the `class` attribute, almost all elements can have this. **But**, unlike class, this does not group elements together. **It uniquely identifies an element** 92 | 93 | What this means is that it cannot be used for more than one element on a page and one element cannot have multiple ids. 94 | 95 | ```html 96 | ... 97 |

    I am only red

    98 |

    I am only big

    99 |

    I am red and big

    100 | ... 101 | ``` 102 | 103 | To use it in CSS, the **hash (#)** prefix is used like so; 104 | 105 | ```css 106 | #red { 107 | color: red; 108 | } 109 | ``` 110 | 111 | With the above, only the paragraph with the id of red will be styled. 112 | 113 | ### 4. Universal Selector 114 | 115 | This worlds like a **wild card character (\*)** used for selecting all elements. 116 | 117 | ```css 118 | * { 119 | font-size: 30px; 120 | text-decoration: underline; 121 | } 122 | ``` 123 | 124 | This selects all elements in the page and applies the style declarations. 125 | 126 | ### 5. Attribute Method 127 | 128 | We can target elements by leveraging on their properties - attributes. For example; 129 | 130 | ```css 131 | h1[align] { 132 | color: brown; 133 | } 134 | h1[align="left"] { 135 | color: pink; 136 | } 137 | ``` 138 | 139 | The first declaration targets all `h1` elements with an align attribute irrespective of it's value while the second declaration targets all `h1` elements with a value of 'left' for the align attribute. 140 | 141 | ### Notes. 142 | 143 | - ids are usually used by Javascript to target elements. It is advisable to use only classes for styles as only one id can be used in a document. 144 | - When two values are declared for a property in a selector, the last one has the highest priority. In the code below, all paragraphs would be green; 145 | 146 | ```css 147 | p { 148 | color: red; 149 | color: green; 150 | } 151 | ``` 152 | 153 | - These methods can be mixed with themselves for selecting elements e.g 154 | 155 | ```css 156 | .container div { 157 | /* Some styles */ 158 | } 159 | .container * { 160 | /* styles */ 161 | } 162 | ``` 163 | 164 | In the first code, the styles declared only affect divs which are children (or grandchildren) of elements with the 'container' class. While in the second, the styles affects all elements in the element with the 'container' class. 165 | 166 | - You can declare the same style for multiple selectors. The selectors are seperated with commas. e.g 167 | 168 | ```css 169 | .container p, 170 | footer p { 171 | font-size: 50px; 172 | color: purple; 173 | } 174 | .container p { 175 | text-decoration: none; 176 | } 177 | .footer p { 178 | border: solid; 179 | } 180 | ``` 181 | 182 | What this style does, is that it defines the same values of font-size and color for the two selectors. Then, it individually defines the text-decoration of the first selector and the border of the second selector. 183 | 184 | --- 185 | 186 | With these methods, you can correctly specify the elements you want to style. There are more advanced methods for selecting. Kindly check out [CSS Selectors Reference](https://w3schools.com/cssref/css_selectors.asp) 187 | -------------------------------------------------------------------------------- /src/pages/p/css/css-specificity.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CSS Specificity 3 | category: css 4 | date: 2019-12-31 5 | authorID: 1 6 | pageDescription: >- 7 | CSS Specificity is a set of rules used by CSS in browsers to determine which style declaration (among other declarations) would be applied to an element. As expected, the higher the specificity of a selector method, the more prioritized it is. 8 | pageKeywords: "css, css specificity, selector specificity, specificity, css selector specificity, !important" 9 | cover: >- 10 | https://res.cloudinary.com/dillionmegida/image/upload/v1577711058/images/thewebfor5/css-specificity_jtb1ub.jpg 11 | tags: ["css"] 12 | --- 13 | 14 | You may have set some style declarations in CSS but didn't get the desired result. After so many trials, you opt-in for the usage of almighty `!important`. It works but this isn't the best solution. 15 | 16 | The eror is often due to conflicting declarations of which only one would be used. To determine which one would be used so as to get exactly what you want, you'd need to understand the specificity of selector methods. 17 | 18 | ## CSS Specificity 19 | 20 | This is a set of rules used by CSS in browsers to determine which style declaration (among other declarations) would be applied to an element. As expected, the higher the specificity of a selector method, the more prioritized it is. 21 | 22 | ## Benefits 23 | 24 | - It helps you specify which style would be applied. 25 | - It reduces the complexity of your codes. 26 | - It makes it easier to edit styles. 27 | 28 | ## Specificity in action 29 | 30 | Examine the following codes; 31 | 32 | ```html 33 | 34 |

    Paragraph

    35 | ``` 36 | 37 | ```css 38 | /* css file */ 39 | p { 40 | color: pink; 41 | } 42 | .red { 43 | color: red; 44 | } 45 | #blue { 46 | color: blue; 47 | } 48 | p.red { 49 | color: orange; 50 | } 51 | .red#blue { 52 | color: green; 53 | } 54 | ``` 55 | 56 | I'd give you three seconds to guess the color of the paragraph element. 57 | 58 | 3, 2, 1...okay. You probably got it. 59 | 60 | The paragraph would be of orange color. **How?** Let's learn how to calculate specificity. 61 | 62 | ## Calculating CSS Specificity 63 | 64 | - You need to know the value of selectors. 65 | - Inline styles takes priority over declarations in external stylesheets or internal style declarations (between `