├── core ├── index.ts ├── typings │ └── index.ts ├── models │ ├── author.ts │ ├── category.ts │ ├── index.ts │ └── post.ts ├── integrations │ ├── index.ts │ ├── typings.ts │ └── contentful.service.ts ├── api.spec.ts └── api.ts ├── now.json ├── .vscode └── settings.json ├── next-env.d.ts ├── public ├── favicon.ico └── zeit.svg ├── setup-contentful.png ├── tsconfig.jest.json ├── pages ├── 404.tsx ├── _error.tsx ├── _app.tsx ├── index.tsx ├── category │ └── [id].tsx └── post │ └── [id].tsx ├── components ├── index.ts ├── footer.spec.tsx ├── not-found.spec.tsx ├── subscription.spec.tsx ├── not-found.tsx ├── __snapshots__ │ ├── not-found.spec.tsx.snap │ ├── footer.spec.tsx.snap │ ├── subscription.spec.tsx.snap │ └── nav.spec.tsx.snap ├── nav.spec.tsx ├── footer.tsx ├── nav.tsx └── subscription.tsx ├── .prettierrc.js ├── .editorconfig ├── next.config.js ├── tests └── setupTests.ts ├── .gitignore ├── config └── index.ts ├── config.json ├── .eslintrc.js ├── .github └── workflows │ └── test-lint.yml ├── tsconfig.json ├── jest.config.js ├── README.md ├── package.json ├── bin └── setup │ └── contentful.js ├── styles ├── main.scss └── bootstrap-user-variables.scss └── schemas └── contentful.json /core/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api'; 2 | -------------------------------------------------------------------------------- /now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "CONTENTFUL" 4 | ] 5 | } -------------------------------------------------------------------------------- /core/typings/index.ts: -------------------------------------------------------------------------------- 1 | export enum Integrations { 2 | CONTENTFUL = 'contentful', 3 | } 4 | -------------------------------------------------------------------------------- /core/models/author.ts: -------------------------------------------------------------------------------- 1 | export interface Author { 2 | name: string; 3 | photo: string; 4 | } 5 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /core/integrations/index.ts: -------------------------------------------------------------------------------- 1 | export * from './contentful.service'; 2 | export * from './typings'; 3 | -------------------------------------------------------------------------------- /core/models/category.ts: -------------------------------------------------------------------------------- 1 | export interface Category { 2 | name: string; 3 | slug: string; 4 | } 5 | -------------------------------------------------------------------------------- /core/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './author'; 2 | export * from './category'; 3 | export * from './post'; 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxigimenez/next-medium-blog-boilerplate/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /setup-contentful.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxigimenez/next-medium-blog-boilerplate/HEAD/setup-contentful.png -------------------------------------------------------------------------------- /tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "jsx": "react" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /pages/404.tsx: -------------------------------------------------------------------------------- 1 | import { NotFound } from '@/components'; 2 | 3 | const Page404 = () => ; 4 | export default Page404; 5 | -------------------------------------------------------------------------------- /pages/_error.tsx: -------------------------------------------------------------------------------- 1 | import { NotFound } from '@/components'; 2 | 3 | const Error = () => ; 4 | export default Error; 5 | -------------------------------------------------------------------------------- /components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './footer'; 2 | export * from './nav'; 3 | export * from './not-found'; 4 | export * from './subscription'; 5 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | printWidth: 120, 6 | tabWidth: 2, 7 | }; 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | CONTENTFUL_SPACE_ID: process.env.CONTENTFUL_SPACE_ID, 4 | CONTENTFUL_ACCESS_TOKEN: process.env.CONTENTFUL_ACCESS_TOKEN 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/setupTests.ts: -------------------------------------------------------------------------------- 1 | import Enzyme from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-react-16'; 3 | 4 | // Configure Enzyme with React 16 adapter 5 | Enzyme.configure({ adapter: new Adapter() }); 6 | -------------------------------------------------------------------------------- /core/models/post.ts: -------------------------------------------------------------------------------- 1 | import { Category } from './category'; 2 | import { Author } from './author'; 3 | 4 | export interface Post { 5 | title: string; 6 | slug: string; 7 | heroImage: string; 8 | shortBody: string; 9 | body: string; 10 | publishedAt: string; 11 | author: Author; 12 | category: Category; 13 | readingTime: string; 14 | } 15 | -------------------------------------------------------------------------------- /components/footer.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import { Footer } from './footer'; 4 | 5 | describe('footer', () => { 6 | describe('snapshots', () => { 7 | test('render', () => { 8 | const component = shallow(); 9 | expect(component).toMatchSnapshot(); 10 | }); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /components/not-found.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import { NotFound } from './not-found'; 4 | 5 | describe('not-found', () => { 6 | describe('snapshots', () => { 7 | test('render default', () => { 8 | const component = shallow(); 9 | expect(component).toMatchSnapshot(); 10 | }); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | .env* 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | .now 28 | .vercel -------------------------------------------------------------------------------- /config/index.ts: -------------------------------------------------------------------------------- 1 | import config from '../config.json'; 2 | import { Integrations } from '@/core/typings'; 3 | 4 | interface Config { 5 | name: string; 6 | title: string; 7 | domain: string; 8 | googleAnalytics?: string; 9 | subscription: { 10 | enabled: boolean; 11 | url: string; 12 | success: string; 13 | error: string; 14 | }; 15 | integration: Integrations; 16 | } 17 | 18 | export default config as Config; 19 | -------------------------------------------------------------------------------- /core/integrations/typings.ts: -------------------------------------------------------------------------------- 1 | import { Post, Category } from '../models'; 2 | 3 | export interface IntegrationService { 4 | getPosts(): Promise; 5 | getPostBySlug(slug: string): Promise; 6 | getPostsByCategory(categorySlug: string): Promise; 7 | getCategories(): Promise; 8 | getCategory(slug: string): Promise; 9 | getPostsPaths(): Promise; 10 | getCategoriesPaths(): Promise; 11 | } 12 | -------------------------------------------------------------------------------- /components/subscription.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import { Subscription } from './subscription'; 4 | 5 | describe('subscription', () => { 6 | describe('snapshots', () => { 7 | let component; 8 | beforeEach(() => { 9 | component = shallow(); 10 | }); 11 | 12 | test('render default', () => { 13 | expect(component).toMatchSnapshot(); 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Demo Blog", 3 | "title": "Demo Blog - Next.js medium style boilerplate", 4 | "domain": "", 5 | "googleAnalytics": "UA-133302217-1", 6 | "subscription": { 7 | "enabled": true, 8 | "url": "https://sheetapi.co/apis/8oSEuumQgFaBfkbdNktgtw", 9 | "success": "Thank you for signing up. You will be the first to know about new releases", 10 | "error": "An error occurred, please try again" 11 | }, 12 | "integration": "contentful" 13 | } 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | ecmaVersion: 2020, 5 | sourceType: 'module', 6 | ecmaFeatures: { 7 | jsx: true 8 | } 9 | }, 10 | settings: { 11 | react: { 12 | version: 'detect' 13 | } 14 | }, 15 | extends: [ 16 | 'plugin:react/recommended', 17 | 'plugin:@typescript-eslint/recommended', 18 | 'prettier/@typescript-eslint', 19 | 'plugin:prettier/recommended', 20 | 'plugin:jsx-a11y/recommended', 21 | ], 22 | rules: { 23 | 'react/react-in-jsx-scope': 'off' 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /core/api.spec.ts: -------------------------------------------------------------------------------- 1 | import { API } from './api'; 2 | import { ContentfulService } from './integrations'; 3 | 4 | /** 5 | * @TODO move into a mocks folder 6 | */ 7 | jest.mock('./integrations', () => { 8 | return { 9 | ContentfulService: jest.fn(), 10 | }; 11 | }); 12 | 13 | describe('API', () => { 14 | it('should be defined', () => { 15 | expect(API).toBeDefined(); 16 | }); 17 | 18 | it('should be able to intialize with default integration', () => { 19 | const api = new API(); 20 | expect(api).toBeInstanceOf(API); 21 | expect(ContentfulService).toHaveBeenCalledTimes(1); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /components/not-found.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Head from 'next/head'; 3 | import config from '@/config'; 4 | 5 | export const NotFound = () => ( 6 | <> 7 | 8 | {config.title} 9 | 10 | 11 | 404 12 | 13 | 14 | The page you requested was not found. 15 | 16 | 17 | 18 | > 19 | ); 20 | -------------------------------------------------------------------------------- /.github/workflows/test-lint.yml: -------------------------------------------------------------------------------- 1 | name: Test & Lint 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | types: [ opened, synchronize ] 8 | 9 | jobs: 10 | dependencies: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - run: yarn install --frozen-lockfile --check-files 15 | - uses: actions/cache@v1 16 | id: cache-dependencies 17 | with: 18 | path: '.' 19 | key: ${{ github.sha }} 20 | 21 | test-lint: 22 | runs-on: ubuntu-latest 23 | needs: dependencies 24 | steps: 25 | - uses: actions/cache@v1 26 | id: restore-dependencies 27 | with: 28 | path: '.' 29 | key: ${{ github.sha }} 30 | - run: yarn test 31 | - run: yarn lint 32 | -------------------------------------------------------------------------------- /public/zeit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /components/__snapshots__/not-found.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`not-found snapshots render default 1`] = ` 4 | 5 | 6 | 7 | Demo Blog - Next.js medium style boilerplate 8 | 9 | 10 | 18 | 21 | 404 22 | 23 | 26 | 30 | The page you requested was not found. 31 | 32 | 33 | 34 | 35 | `; 36 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve", 20 | "baseUrl": ".", 21 | "paths": { 22 | "@/components": [ 23 | "./components/index.ts" 24 | ], 25 | "@/core/*": [ 26 | "./core/*" 27 | ], 28 | "@/config": [ 29 | "./config/index.ts" 30 | ] 31 | } 32 | }, 33 | "exclude": [ 34 | "node_modules" 35 | ], 36 | "include": [ 37 | "next-env.d.ts", 38 | "**/*.ts", 39 | "**/*.tsx" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /components/nav.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import { Nav } from './nav'; 4 | 5 | describe('nav', () => { 6 | describe('snapshots', () => { 7 | test('render without categories', () => { 8 | const component = shallow(); 9 | expect(component).toMatchSnapshot(); 10 | }); 11 | 12 | test('render with categories', () => { 13 | const categories = [ 14 | { 15 | name: 'Category 1', 16 | slug: 'category-1', 17 | }, 18 | { 19 | name: 'Category 2', 20 | slug: 'category-2', 21 | }, 22 | ]; 23 | const component = shallow(); 24 | expect(component).toMatchSnapshot(); 25 | }); 26 | 27 | test('render if categories undefined', () => { 28 | const component = shallow(); 29 | expect(component).toMatchSnapshot(); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /components/footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import config from '@/config'; 3 | 4 | export const Footer = () => ( 5 | 6 | 31 | 32 | ); 33 | -------------------------------------------------------------------------------- /components/__snapshots__/footer.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`footer snapshots render 1`] = ` 4 | 7 | 45 | 46 | `; 47 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const { pathsToModuleNameMapper } = require("ts-jest/utils"); 2 | const { compilerOptions } = require('./tsconfig.json'); 3 | 4 | module.exports = { 5 | testEnvironment: "node", 6 | roots: [ 7 | "/components", 8 | "/core", 9 | "/config", 10 | "/pages" 11 | ], 12 | preset: 'ts-jest', 13 | setupFilesAfterEnv: ["/tests/setupTests.ts"], 14 | transform: { 15 | "^.+\\.tsx?$": "ts-jest" 16 | }, 17 | testMatch: [ 18 | "**/*.(test|spec).(ts|tsx)" 19 | ], 20 | moduleFileExtensions: [ 21 | "ts", 22 | "tsx", 23 | "js", 24 | "jsx" 25 | ], 26 | testPathIgnorePatterns: ["/.next/", "/node_modules/"], 27 | snapshotSerializers: ["enzyme-to-json/serializer"], 28 | // https://github.com/zeit/next.js/issues/8663#issue-490553899 29 | globals: { 30 | // we must specify a custom tsconfig for tests because we need the typescript transform 31 | // to transform jsx into js rather than leaving it jsx such as the next build requires. you 32 | // can see this setting in tsconfig.jest.json -> "jsx": "react" 33 | "ts-jest": { 34 | tsConfig: "/tsconfig.jest.json" 35 | } 36 | }, 37 | coverageReporters: ["text", "html"], 38 | moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { 39 | prefix: '/' 40 | }), 41 | } 42 | -------------------------------------------------------------------------------- /components/__snapshots__/subscription.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`subscription snapshots render default 1`] = ` 4 | 7 | 10 | 13 | 16 | Become a member 17 | 18 | Get the latest news right in your inbox. We never spam! 19 | 20 | 23 | 26 | 29 | 37 | 38 | 41 | 47 | Subscribe 48 | 49 | 50 | 51 | 52 | 53 | 54 | `; 55 | -------------------------------------------------------------------------------- /core/api.ts: -------------------------------------------------------------------------------- 1 | import { Post, Category } from './models'; 2 | import { ContentfulService, IntegrationService } from './integrations'; 3 | import { Integrations } from './typings'; 4 | import config from '@/config'; 5 | 6 | export class API { 7 | private _client: IntegrationService; 8 | 9 | constructor() { 10 | const integration = this._resolveIntegration(); 11 | switch (integration) { 12 | case Integrations.CONTENTFUL: 13 | this._client = new ContentfulService(); 14 | break; 15 | default: 16 | throw new Error(`Invalid integration: ${integration}`); 17 | } 18 | } 19 | 20 | getPosts(): Promise { 21 | return this._client.getPosts(); 22 | } 23 | 24 | getPostBySlug(slug: string): Promise { 25 | return this._client.getPostBySlug(slug); 26 | } 27 | 28 | getPostsByCategory(categorySlug: string): Promise { 29 | return this._client.getPostsByCategory(categorySlug); 30 | } 31 | 32 | getCategories(): Promise { 33 | return this._client.getCategories(); 34 | } 35 | 36 | getCategory(slug: string): Promise { 37 | return this._client.getCategory(slug); 38 | } 39 | 40 | getPostsPaths(): Promise { 41 | return this._client.getPostsPaths(); 42 | } 43 | 44 | getCategoriesPaths(): Promise { 45 | return this._client.getCategoriesPaths(); 46 | } 47 | 48 | private _resolveIntegration(): Integrations { 49 | if (config.integration && Object.values(Integrations).includes(config.integration)) { 50 | return config.integration; 51 | } 52 | return Integrations.CONTENTFUL; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js Medium style boilerplate blog 2 | 3 | > This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/zeit/next.js/tree/canary/packages/create-next-app). 4 | 5 | ## Data Sources 6 | 7 | This project has been designed to support any data source, under the `core/` folder you can find the models and service structure. 8 | 9 | ### Contentful 10 | 11 | Contentful is the default integration supported at the moment, we also provided a setup script together with a schema to easily get it up and running. 12 | 13 | ## Template 14 | 15 | This project uses [Mudana](https://www.wowthemes.net/mundana-free-html-bootstrap-template/) to achieve the medium style blog. 16 | 17 | ## Getting Started 18 | 19 | ### Install dependencies 20 | 21 | ``` 22 | $ git@github.com:maxigimenez/next-medium-blog-boilerplate.git 23 | $ yarn install 24 | ``` 25 | 26 | ### Setup models 27 | 28 | #### Contentful 29 | 30 | This projects comes with a Contentful schema ready to be used. Using `yarn setup:contentful`: 31 | 32 |  33 | 34 | This command will ask you for a space ID, and access tokens for the Contentful Management and Delivery API and then import the schema defined on "schemas/contentful.json". 35 | 36 | Once the script is done you will be able to launch the blog and see dummy information ready to be changed. 37 | 38 | ## Scripts 39 | 40 | ### `yarn dev` 41 | 42 | Run the project locally. Then open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 43 | 44 | ## Deploy 45 | 46 | The repository comes with a simple `now.json` configuration, so we recommend to use [Zeit.co](https://zeit.co) to host the blog. 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-medium-blog-boilerplate", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "setup:contentful": "node bin/setup/contentful.js", 10 | "test": "jest", 11 | "test:watch": "jest --watch", 12 | "test:coverage": "jest --coverage", 13 | "lint:fix": "yarn lint --fix", 14 | "lint": "eslint '{pages,components,config,core}/**/*.{js,ts,tsx}' --quiet" 15 | }, 16 | "dependencies": { 17 | "bootstrap": "^4.4.1", 18 | "contentful": "^7.14.2", 19 | "dayjs": "^1.8.24", 20 | "next": "^9.5.4", 21 | "react": "^16.13.1", 22 | "react-dom": "^16.13.1", 23 | "react-markdown": "^4.3.1", 24 | "reading-time": "^1.2.0" 25 | }, 26 | "devDependencies": { 27 | "@types/enzyme": "^3.10.5", 28 | "@types/enzyme-adapter-react-16": "^1.0.6", 29 | "@types/jest": "^25.2.1", 30 | "@types/node": "^13.11.1", 31 | "@types/react": "^16.9.34", 32 | "@typescript-eslint/eslint-plugin": "^3.6.1", 33 | "@typescript-eslint/parser": "^3.6.1", 34 | "chalk": "^4.0.0", 35 | "contentful-import": "^7.7.8", 36 | "enzyme": "^3.11.0", 37 | "enzyme-adapter-react-16": "^1.15.2", 38 | "enzyme-to-json": "^3.4.4", 39 | "eslint": "^7.5.0", 40 | "eslint-config-prettier": "^6.11.0", 41 | "eslint-plugin-jsx-a11y": "^6.3.1", 42 | "eslint-plugin-prettier": "^3.1.4", 43 | "eslint-plugin-react": "^7.20.3", 44 | "eslint-plugin-react-hooks": "^4.0.8", 45 | "inquirer": "^7.1.0", 46 | "jest": "^25.5.4", 47 | "prettier": "^2.0.5", 48 | "sass": "^1.26.3", 49 | "ts-jest": "^25.5.0", 50 | "typescript": "^3.8.3" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import App from 'next/app'; 2 | import Head from 'next/head'; 3 | 4 | import '../styles/main.scss'; 5 | 6 | import { Nav, Footer } from '@/components'; 7 | import config from '@/config'; 8 | import { API } from '@/core/api'; 9 | import { Category } from '@/core/models'; 10 | 11 | const CustomApp = ({ 12 | Component, 13 | pageProps, 14 | categories, 15 | isErrorPage, 16 | }: { 17 | Component: any; 18 | pageProps: any; 19 | categories: Category[]; 20 | isErrorPage: boolean; 21 | }) => { 22 | return ( 23 | <> 24 | 25 | 29 | {/* Enable google analytics tracking */} 30 | {!!config.googleAnalytics && ( 31 | 39 | )} 40 | {!!config.googleAnalytics && ( 41 | 42 | )} 43 | 44 | 45 | {!isErrorPage && } 46 | 47 | 48 | 49 | {!isErrorPage && } 50 | > 51 | ); 52 | }; 53 | 54 | CustomApp.getInitialProps = async (context) => { 55 | const apiRef = new API(); 56 | const appProps = await App.getInitialProps(context); 57 | const categories = await apiRef.getCategories(); 58 | const isErrorPage = context.ctx.res.statusCode === 404 || false; 59 | return { ...appProps, categories, isErrorPage }; 60 | }; 61 | 62 | export default CustomApp; 63 | -------------------------------------------------------------------------------- /components/nav.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'next/link'; 3 | import config from '@/config'; 4 | import { Category } from '@/core/models'; 5 | 6 | type Props = { 7 | categories: Category[]; 8 | }; 9 | 10 | export const Nav = ({ categories }: Props) => ( 11 | 12 | 13 | 14 | 15 | {config.name} 16 | 17 | 18 | 27 | 28 | 29 | 30 | 31 | {categories && 32 | categories.map((category: Category) => { 33 | return ( 34 | 35 | 36 | 37 | {category.name} 38 | 39 | 40 | 41 | ); 42 | })} 43 | 44 | 45 | 46 | 52 | Get this blog boilerplate 53 | 54 | 55 | 56 | 57 | 58 | 59 | ); 60 | -------------------------------------------------------------------------------- /components/subscription.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import config from '@/config'; 3 | 4 | interface AlertMeta { 5 | class: string; 6 | message: string; 7 | } 8 | 9 | export const Subscription = () => { 10 | const [email, setEmail] = useState(''); 11 | const [blocked, setBlocked] = useState(false); 12 | const [alert, setAlert] = useState(); 13 | 14 | const subscribe = async () => { 15 | setBlocked(true); 16 | setAlert(null); 17 | try { 18 | const data = await fetch(config.subscription.url, { 19 | method: 'POST', 20 | body: JSON.stringify({ email, date: new Date() }), 21 | headers: { 22 | 'Content-Type': 'application/json', 23 | }, 24 | }); 25 | const json = await data.json(); 26 | if (json) { 27 | setAlert({ 28 | class: 'success', 29 | message: config.subscription.success, 30 | }); 31 | } 32 | } catch (e) { 33 | setAlert({ 34 | class: 'danger', 35 | message: config.subscription.error, 36 | }); 37 | setBlocked(false); 38 | } 39 | }; 40 | 41 | return ( 42 | 43 | 44 | 45 | Become a member 46 | Get the latest news right in your inbox. We never spam! 47 | 48 | 49 | {alert && ( 50 | 51 | {alert.message} 52 | 53 | )} 54 | 55 | 56 | 57 | setEmail(e.target.value)} 60 | type="email" 61 | className="form-control" 62 | placeholder="Enter your e-mail address" 63 | disabled={blocked} 64 | /> 65 | 66 | 67 | 68 | Subscribe 69 | 70 | 71 | 72 | 73 | 74 | 75 | ); 76 | }; 77 | -------------------------------------------------------------------------------- /bin/setup/contentful.js: -------------------------------------------------------------------------------- 1 | const spaceImport = require('contentful-import'); 2 | const schema = require('../../schemas/contentful.json'); 3 | const inquirer = require('inquirer'); 4 | const chalk = require('chalk'); 5 | const path = require('path'); 6 | const { writeFileSync } = require('fs'); 7 | 8 | const argv = require('yargs-parser')(process.argv.slice(2)); 9 | 10 | console.log(` 11 | To set up this project you need to provide your Space ID 12 | and the belonging API access tokens. 13 | You can find all the needed information in your Contentful space under: 14 | ${chalk.yellow( 15 | `app.contentful.com ${chalk.red('->')} Space Settings ${chalk.red( 16 | '->' 17 | )} API keys` 18 | )} 19 | The ${chalk.green('Content Management API Token')} 20 | will be used to import and write data to your space. 21 | The ${chalk.green('Content Delivery API Token')} 22 | will be used to ship published production-ready content in your Gatsby app. 23 | The ${chalk.green('Content Preview API Token')} 24 | will be used to show not published data in your development environment. 25 | Ready? Let's do it! 🎉 26 | `); 27 | 28 | const questions = [ 29 | { 30 | name: 'spaceId', 31 | message: 'Your Space ID', 32 | when: !argv.spaceId && !process.env.CONTENTFUL_SPACE_ID, 33 | validate: input => 34 | /^[a-z0-9]{12}$/.test(input) || 35 | 'Space ID must be 12 lowercase characters', 36 | }, 37 | { 38 | name: 'managementToken', 39 | when: !argv.managementToken && !process.env.CONTENTFUL_MANAGEMENT_TOKEN, 40 | message: 'Your Content Management API access token', 41 | }, 42 | { 43 | name: 'accessToken', 44 | when: !argv.accessToken && !process.env.CONTENTFUL_ACCESS_TOKEN, 45 | message: 'Your Content Delivery API access token', 46 | } 47 | ]; 48 | 49 | inquirer 50 | .prompt(questions) 51 | .then(({ spaceId, managementToken, accessToken }) => { 52 | const { 53 | CONTENTFUL_SPACE_ID, 54 | CONTENTFUL_ACCESS_TOKEN, 55 | CONTENTFUL_MANAGEMENT_TOKEN 56 | } = process.env; 57 | 58 | // env vars are given precedence followed by args provided to the setup 59 | // followed by input given to prompts displayed by the setup script 60 | spaceId = CONTENTFUL_SPACE_ID || argv.spaceId || spaceId; 61 | managementToken = CONTENTFUL_MANAGEMENT_TOKEN || argv.managementToken || managementToken; 62 | accessToken = CONTENTFUL_ACCESS_TOKEN || argv.accessToken || accessToken; 63 | 64 | console.log('Writing config file...'); 65 | const configFiles = [`.env`].map(file => 66 | path.join(__dirname, '../..', file) 67 | ); 68 | 69 | const fileContents = 70 | [ 71 | `# Do NOT commit this file to source control`, 72 | `CONTENTFUL_SPACE_ID=${spaceId}`, 73 | `CONTENTFUL_ACCESS_TOKEN=${accessToken}`, 74 | ].join('\n') + '\n'; 75 | 76 | configFiles.forEach(file => { 77 | writeFileSync(file, fileContents, 'utf8') 78 | console.log(`Config file ${chalk.yellow(file)} written`); 79 | }) 80 | return { spaceId, managementToken }; 81 | }) 82 | .then(({ spaceId, managementToken }) => 83 | spaceImport({ spaceId, managementToken, content: schema }) 84 | ) 85 | .then((_, error) => { 86 | console.log( 87 | `All set! You can now run ${chalk.yellow( 88 | 'yarn dev' 89 | )} to see it in action.` 90 | ); 91 | }) 92 | .catch(error => console.error(error)); 93 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | import Link from 'next/link'; 3 | import ReactMarkdown from 'react-markdown'; 4 | 5 | import { Post } from '@/core/models'; 6 | import { API } from '@/core/api'; 7 | import config from '@/config'; 8 | 9 | const Home = ({ posts }: { posts: Post[] }) => { 10 | const [first] = posts; 11 | 12 | return ( 13 | <> 14 | 15 | {config.title} 16 | 17 | 18 | {first && ( 19 | 20 | 21 | 22 | 23 | 24 | {first.title} 25 | 26 | 27 | 28 | Read More 29 | 30 | 31 | 32 | 40 | 41 | 42 | 43 | 44 | )} 45 | 46 | 47 | 48 | 49 | 50 | All Stories 51 | 52 | {posts.map((post) => { 53 | return ( 54 | 55 | 56 | 57 | 58 | 59 | {post.title} 60 | 61 | 62 | 63 | 64 | 65 | {post.author.name} in {post.category.name} 66 | 67 | 68 | {post.publishedAt} · {post.readingTime} 69 | 70 | 71 | 72 | 73 | ); 74 | })} 75 | 76 | 77 | 78 | > 79 | ); 80 | }; 81 | 82 | export const getStaticProps = async () => { 83 | const apiRef = new API(); 84 | const posts = await apiRef.getPosts(); 85 | return { 86 | props: { 87 | posts, 88 | }, 89 | revalidate: 1, 90 | }; 91 | }; 92 | 93 | export default Home; 94 | -------------------------------------------------------------------------------- /pages/category/[id].tsx: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | import Link from 'next/link'; 3 | 4 | import ReactMarkdown from 'react-markdown'; 5 | 6 | import { API } from '@/core/api'; 7 | import config from '@/config'; 8 | import { Post, Category as CategoryModel } from '@/core/models'; 9 | import { Subscription } from '@/components'; 10 | 11 | type Props = { 12 | posts?: Post[]; 13 | category?: CategoryModel; 14 | }; 15 | 16 | const Category = ({ posts, category }: Props) => { 17 | const [first] = posts; 18 | 19 | return ( 20 | <> 21 | 22 | {config.title} 23 | 24 | 25 | 26 | 27 | 28 | 29 | Featured in {category.name} 30 | 31 | {first && ( 32 | 33 | 36 | 37 | 38 | 39 | 40 | {first.title} 41 | 42 | 43 | 44 | 45 | 46 | 47 | {first.author.name} in {first.category.name} 48 | 49 | 50 | {first.publishedAt} · {first.readingTime} 51 | 52 | 53 | 54 | 55 | )} 56 | 57 | Latest 58 | 59 | {posts.map((post: Post) => { 60 | return ( 61 | 62 | 63 | 64 | 65 | 66 | {post.title} 67 | 68 | 69 | 70 | 71 | 72 | {post.author.name} in {post.category.name} 73 | 74 | 75 | {post.publishedAt} · {post.readingTime} 76 | 77 | 78 | 79 | 80 | ); 81 | })} 82 | 83 | 84 | 85 | 86 | {config.subscription.enabled && ( 87 | 88 | 89 | 90 | )} 91 | > 92 | ); 93 | }; 94 | 95 | export const getStaticPaths = async () => { 96 | const apiRef = new API(); 97 | const slugs = await apiRef.getCategoriesPaths(); 98 | return { 99 | paths: slugs.map((slug) => `/category/${slug}`), 100 | fallback: false, 101 | }; 102 | }; 103 | 104 | export const getStaticProps = async ({ params }) => { 105 | const apiRef = new API(); 106 | const category = await apiRef.getCategory(params.id); 107 | const posts = await apiRef.getPostsByCategory(params.id); 108 | return { 109 | props: { 110 | posts, 111 | category, 112 | }, 113 | revalidate: 1, 114 | }; 115 | }; 116 | 117 | export default Category; 118 | -------------------------------------------------------------------------------- /core/integrations/contentful.service.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from 'contentful'; 2 | import { IntegrationService } from './typings'; 3 | import { Post, Category } from '../models'; 4 | import readingTime from 'reading-time'; 5 | import dayjs from 'dayjs'; 6 | 7 | enum ContentType { 8 | POST = 'post', 9 | CATEGORY = 'category', 10 | } 11 | 12 | export class ContentfulService implements IntegrationService { 13 | private _client = createClient({ 14 | space: process.env.CONTENTFUL_SPACE_ID, 15 | accessToken: process.env.CONTENTFUL_ACCESS_TOKEN, 16 | }); 17 | 18 | getPosts(): Promise { 19 | return this._getPosts().then((items) => items.map((item) => createPost(item))); 20 | } 21 | 22 | getPostBySlug(slug: string): Promise { 23 | return new Promise(async (resolve, reject) => { 24 | try { 25 | const { items }: any = await this._client.getEntries({ 26 | content_type: ContentType.POST, 27 | 'fields.slug[in]': slug, 28 | }); 29 | const [post] = items; 30 | if (!post) { 31 | return reject(); 32 | } 33 | return resolve(createPost(post)); 34 | } catch (e) { 35 | return reject(e); 36 | } 37 | }); 38 | } 39 | 40 | getPostsByCategory(categorySlug: string): Promise { 41 | return new Promise(async (resolve, reject) => { 42 | try { 43 | const { items } = await this._client.getEntries({ 44 | content_type: ContentType.POST, 45 | 'fields.category.fields.slug[match]': categorySlug, 46 | 'fields.category.sys.contentType.sys.id': ContentType.CATEGORY, 47 | order: '-fields.publishedAt', 48 | }); 49 | resolve(items.map((item) => createPost(item))); 50 | } catch (e) { 51 | return reject(e); 52 | } 53 | }); 54 | } 55 | 56 | getCategories(): Promise { 57 | return this._getCategories().then((items) => items.map((item) => createCategory(item))); 58 | } 59 | 60 | getCategory(slug: string): Promise { 61 | return new Promise(async (resolve, reject) => { 62 | try { 63 | const { items }: any = await this._client.getEntries({ 64 | content_type: ContentType.CATEGORY, 65 | 'fields.slug[in]': slug, 66 | }); 67 | const [category] = items; 68 | if (!category) { 69 | return reject(); 70 | } 71 | return resolve(createCategory(category)); 72 | } catch (e) { 73 | return reject(e); 74 | } 75 | }); 76 | } 77 | 78 | getPostsPaths(): Promise { 79 | return this._getPosts('fields.slug').then((items) => items.map((item) => item.fields.slug)); 80 | } 81 | 82 | getCategoriesPaths(): Promise { 83 | return this._getCategories('fields.slug').then((items) => items.map((item) => item.fields.slug)); 84 | } 85 | 86 | private _getPosts(select?: string): Promise { 87 | return new Promise(async (resolve, reject) => { 88 | try { 89 | const { items } = await this._client.getEntries({ 90 | content_type: ContentType.POST, 91 | order: '-fields.publishedAt', 92 | select: select, 93 | }); 94 | resolve(items); 95 | } catch (e) { 96 | return reject(e); 97 | } 98 | }); 99 | } 100 | 101 | private _getCategories(select?: string): Promise { 102 | return new Promise(async (resolve, reject) => { 103 | try { 104 | const { items } = await this._client.getEntries({ 105 | content_type: ContentType.CATEGORY, 106 | select: select, 107 | }); 108 | resolve(items); 109 | } catch (e) { 110 | return reject(e); 111 | } 112 | }); 113 | } 114 | } 115 | 116 | const createPost = (data: any): Post => { 117 | const { fields } = data; 118 | const { title, slug, shortBody, body, publishedAt, hero } = fields; 119 | return { 120 | title, 121 | slug, 122 | shortBody, 123 | body, 124 | publishedAt: dayjs(publishedAt).format('MMMM DD'), 125 | heroImage: hero.fields.file.url, 126 | author: { 127 | name: fields.author.fields.name, 128 | photo: fields.author.fields.photo.fields.file.url, 129 | }, 130 | category: { 131 | name: fields.category.fields.name, 132 | slug: fields.category.fields.slug, 133 | }, 134 | readingTime: readingTime(body).text, 135 | }; 136 | }; 137 | 138 | const createCategory = (data: any): Category => { 139 | const { name, slug } = data.fields; 140 | return { 141 | name, 142 | slug, 143 | }; 144 | }; 145 | -------------------------------------------------------------------------------- /pages/post/[id].tsx: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | import Link from 'next/link'; 3 | import ReactMarkdown from 'react-markdown'; 4 | 5 | import { Post as PostModel } from '@/core/models'; 6 | import config from '@/config'; 7 | import { API } from '@/core/api'; 8 | 9 | import { Subscription } from '@/components'; 10 | 11 | const Post = ({ post }: { post: PostModel }) => { 12 | return ( 13 | <> 14 | 15 | 16 | {post.title} - {config.title} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | {post.category.name} 40 | 41 | 42 | 43 | {post.title} 44 | 45 | 46 | 54 | 55 | {post.author.name}{' '} 56 | 57 | {post.publishedAt} · {post.readingTime} 58 | 59 | 60 | 61 | 62 | 63 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | Share this 79 | 80 | 85 | 86 | 87 | 88 | `, 89 | }} 90 | > 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | {config.subscription.enabled && } 99 | 100 | 101 | 102 | > 103 | ); 104 | }; 105 | 106 | export const getStaticPaths = async () => { 107 | const apiRef = new API(); 108 | const slugs = await apiRef.getPostsPaths(); 109 | return { 110 | paths: slugs.map((slug) => `/post/${slug}`), 111 | fallback: false, 112 | }; 113 | }; 114 | 115 | export const getStaticProps = async ({ params }) => { 116 | const apiRef = new API(); 117 | const post = await apiRef.getPostBySlug(params.id); 118 | return { 119 | props: { 120 | post, 121 | }, 122 | revalidate: 1, 123 | }; 124 | }; 125 | 126 | export default Post; 127 | -------------------------------------------------------------------------------- /components/__snapshots__/nav.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`nav snapshots render if categories undefined 1`] = ` 4 | 7 | 10 | 13 | 17 | 18 | Demo Blog 19 | 20 | 21 | 22 | 31 | 34 | 35 | 39 | 42 | 45 | 48 | 54 | Get this blog boilerplate 55 | 56 | 57 | 58 | 59 | 60 | 61 | `; 62 | 63 | exports[`nav snapshots render with categories 1`] = ` 64 | 67 | 70 | 73 | 77 | 78 | Demo Blog 79 | 80 | 81 | 82 | 91 | 94 | 95 | 99 | 102 | 106 | 109 | 113 | Category 1 114 | 115 | 116 | 117 | 121 | 124 | 128 | Category 2 129 | 130 | 131 | 132 | 133 | 136 | 139 | 145 | Get this blog boilerplate 146 | 147 | 148 | 149 | 150 | 151 | 152 | `; 153 | 154 | exports[`nav snapshots render without categories 1`] = ` 155 | 158 | 161 | 164 | 168 | 169 | Demo Blog 170 | 171 | 172 | 173 | 182 | 185 | 186 | 190 | 193 | 196 | 199 | 205 | Get this blog boilerplate 206 | 207 | 208 | 209 | 210 | 211 | 212 | `; 213 | -------------------------------------------------------------------------------- /styles/main.scss: -------------------------------------------------------------------------------- 1 | //==================================== 2 | // Global Imports 3 | //==================================== 4 | 5 | 6 | // 1- Import bootstrap framework 7 | 8 | @import 'bootstrap-user-variables'; 9 | @import '~bootstrap/scss/bootstrap'; 10 | 11 | //==================================== 12 | // Additional theme classes 13 | //==================================== 14 | 15 | // General 16 | body { 17 | overflow-x: hidden; 18 | } 19 | .first-container { 20 | padding-top: 69.75px; 21 | } 22 | .sticky-top { 23 | top: 69.75px; 24 | } 25 | .secondfont, .navbar-brand { 26 | font-family: $font-secondary; 27 | } 28 | img { 29 | max-width:100%; 30 | } 31 | .tofront { 32 | position:relative; 33 | z-index:1; 34 | } 35 | .full-width { 36 | width: 100vw; 37 | position: relative; 38 | margin-left: -50vw; 39 | left: 50%; 40 | } 41 | a, a:hover {transition: all .2s;} 42 | a { 43 | color: $success; 44 | } 45 | a.text-dark:hover { 46 | color: $success !important; 47 | } 48 | .c-pointer:hover { 49 | cursor:pointer; 50 | } 51 | .z-index-1 { 52 | z-index:1; 53 | } 54 | // Typography 55 | 56 | .display-3 { 57 | @include media-breakpoint-down(md) { 58 | font-size: 3.5rem; 59 | } 60 | @include media-breakpoint-down(sm) { 61 | font-size: 2rem; 62 | } 63 | } 64 | 65 | 66 | // Margins 67 | .row.gap-y>.col, .row.gap-y>[class*="col-"] { 68 | padding-top: 15px; 69 | padding-bottom: 15px; 70 | } 71 | .mt-neg5 { 72 | margin-top: -5rem; 73 | } 74 | .ml-neg5 { 75 | margin-left: -5rem; 76 | } 77 | 78 | // Heights 79 | @include media-breakpoint-up(md) { 80 | .h-md-100-v { 81 | height: 100vh; 82 | } 83 | .h-md-100 { 84 | height: 100vh; 85 | } 86 | } 87 | 88 | @include media-breakpoint-only(xl) { 89 | .h-xl-300 { 90 | height: 300px; 91 | } 92 | .h-max-380 { 93 | max-height:380px; 94 | } 95 | } 96 | 97 | // Mixins 98 | @mixin button-variant-link($color){ 99 | color: $color; 100 | } 101 | 102 | // Buttons 103 | .btn-round { 104 | border-radius: $round-radius !important; 105 | } 106 | .btn { 107 | font-family: Source Sans Pro; 108 | @include button-size($btn-padding-y, $btn-padding-x, $font-size-base, $btn-line-height, $btn-border-radius); 109 | &:hover, 110 | &:focus { 111 | box-shadow: $box-shadow; 112 | outline: 0 !important; 113 | } 114 | .badge { 115 | position: absolute; 116 | top: -.625rem; 117 | right: -.3125rem; 118 | } 119 | position:relative; 120 | } 121 | 122 | .btn-lg { 123 | @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $font-size-lg, $btn-line-height-lg, $btn-border-radius-lg); 124 | } 125 | 126 | .btn-sm { 127 | @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $btn-line-height-sm, $btn-border-radius-sm); 128 | } 129 | 130 | .btn-link { 131 | &:hover, 132 | &:focus { 133 | box-shadow:none; 134 | } 135 | } 136 | 137 | @each $color, $value in $theme-colors { 138 | .btn-link.btn-#{$color} { 139 | @include button-variant-link($value); 140 | border-color: transparent; 141 | } 142 | } 143 | .btn-white {background-color:#fff;} 144 | // Inputs 145 | .input-round { 146 | border-radius: $round-radius !important; 147 | } 148 | .input-group.input-round { 149 | input:first-child {border-radius:30px 0 0 30px} 150 | input:last-child {border-radius:0px 30px 30px 0px} 151 | } 152 | 153 | 154 | // Nav 155 | .navbar { 156 | box-shadow:0 0px 1px 0 rgba(0, 0, 0, 0.05); 157 | transition: all .08s; 158 | font-weight: $font-weight-normal; 159 | .highlight .nav-link {color: $primary !important; border:1px solid $success; padding: 0.3rem 1rem; border-radius: 3px;font-size:.93rem;} 160 | .highlight .nav-link:hover {background: $success; color:#fff !important;} 161 | } 162 | .navbar-brand { 163 | margin-right:2rem; 164 | font-size:1.25rem; 165 | } 166 | .scrollednav { 167 | 168 | } 169 | .dropdown-item { 170 | font-weight: $font-weight-base; 171 | } 172 | .dropdown-menu { 173 | border:0; 174 | text-transform:none; 175 | box-shadow: $box-shadow-lg; 176 | &:before { 177 | @include media-breakpoint-up(md) { 178 | content: ''; 179 | top: -8px; 180 | position: absolute; 181 | left: 50px; 182 | border-top: 16px solid #fff; 183 | border-left: 16px solid #fff; 184 | transform: rotate(45deg); 185 | } 186 | } 187 | } 188 | 189 | // Dark links against a light background 190 | .navbar-light { 191 | .navbar-brand { 192 | color: $navbar-light-active-color; 193 | 194 | @include hover-focus { 195 | color: $navbar-light-active-color; 196 | } 197 | } 198 | 199 | .navbar-nav { 200 | .nav-link { 201 | color: $navbar-light-color; 202 | @include hover-focus { 203 | color: $navbar-light-hover-color; 204 | } 205 | 206 | &.disabled { 207 | color: $navbar-light-disabled-color; 208 | } 209 | } 210 | 211 | .show > .nav-link, 212 | .active > .nav-link, 213 | .nav-link.show, 214 | .nav-link.active { 215 | color: $navbar-light-active-color; 216 | } 217 | } 218 | 219 | .navbar-toggler { 220 | color: $navbar-light-color; 221 | border-color: $navbar-light-toggler-border-color; 222 | } 223 | 224 | .navbar-toggler-icon { 225 | background-image: $navbar-light-toggler-icon-bg; 226 | } 227 | 228 | .navbar-text { 229 | color: $navbar-light-color; 230 | a { 231 | color: $navbar-light-active-color; 232 | 233 | @include hover-focus { 234 | color: $navbar-light-active-color; 235 | } 236 | } 237 | } 238 | } 239 | 240 | 241 | // Jumbotron 242 | .jumbotron { 243 | background-size:cover; 244 | padding: 7rem 1rem; 245 | } 246 | @include media-breakpoint-up(lg) { 247 | .jumbotron-lg-withnav {padding-bottom: calc(10rem - 69.75px);} 248 | .jumbotron-lg { padding:10rem 0;} 249 | .jumbotron-xl { padding:15rem 0;} 250 | .jumbotron-xl {min-height:100vh;} 251 | .bottom-align-text-absolute { 252 | position: absolute; 253 | bottom: 30px; 254 | margin:auto; 255 | left: 0; 256 | right:0; 257 | } 258 | } 259 | 260 | // Backgrounds 261 | .bg-black { background-color: $black; } 262 | .overlay { 263 | position: relative; 264 | .container { 265 | position: relative; 266 | } 267 | &:before { 268 | content: ""; 269 | display: block; 270 | height: 100%; 271 | left: 0; 272 | top: 0; 273 | position: absolute; 274 | width: 100%; 275 | } 276 | } 277 | .overlay-black { 278 | &:before { 279 | background-color: rgba(0, 0, 0, 0.5); 280 | } 281 | } 282 | .overlay-blue { 283 | &:before { 284 | background-color: rgba(23, 29, 90, 0.5); 285 | } 286 | } 287 | .overlay-red { 288 | &:before { 289 | background:linear-gradient(0deg,rgba(44,44,44,.2),rgba(224,23,3,.6)); 290 | } 291 | } 292 | .overlay-blue { 293 | &:before { 294 | background-color: rgba(23, 29, 90, 0.5); 295 | } 296 | } 297 | 298 | // SVG fills 299 | 300 | @mixin fill-variant($parent, $color) { 301 | #{$parent} { 302 | fill: $color !important; 303 | } 304 | } 305 | 306 | @each $color, $value in $theme-colors { 307 | @include fill-variant(".bg-#{$color}", $value); 308 | } 309 | 310 | // Cards 311 | .card { 312 | .date { 313 | position: absolute; 314 | top: 20px; 315 | right: 20px; 316 | z-index: 1; 317 | background: $danger; 318 | width: 55px; 319 | height: 55px; 320 | padding: 12.5px 0; 321 | -webkit-border-radius: 100%; 322 | -moz-border-radius: 100%; 323 | border-radius: 100%; 324 | color: #FFFFFF; 325 | font-weight: 700; 326 | text-align: center; 327 | -webkti-box-sizing: border-box; 328 | -moz-box-sizing: border-box; 329 | box-sizing: border-box; 330 | .day { 331 | font-size: 16px; 332 | line-height:1; 333 | } 334 | .month { 335 | font-size: 11px; 336 | text-transform: uppercase; 337 | } 338 | } 339 | a:hover {text-decoration:none; color: $primary;} 340 | } 341 | 342 | .card-pricing .card ul li { 343 | margin-bottom:1.5rem; 344 | } 345 | 346 | // Icons 347 | .iconbox { 348 | border:1px solid; 349 | text-align:center; 350 | display:inline-block; 351 | } 352 | .iconbox.iconsmall { 353 | width:40px; 354 | height:40px; 355 | line-height:40px; 356 | font-size:1rem; 357 | } 358 | .iconbox.iconmedium { 359 | width:60px; 360 | height:60px; 361 | line-height:60px; 362 | font-size:1.8rem; 363 | } 364 | .iconbox.iconlarge { 365 | width:80px; 366 | height:80px; 367 | line-height:80px; 368 | font-size:2.2rem; 369 | } 370 | 371 | // Alerts 372 | @each $color, $value in $theme-colors { 373 | .alert-#{$color} { 374 | @include alert-variant(theme-color-level($color, $alert-bg-level), theme-color-level($color, $alert-border-level), #fff); 375 | } 376 | } 377 | 378 | // Lists 379 | 380 | ul.list-unstyled li {margin-bottom:.3rem;} 381 | ol.list-featured { 382 | counter-reset: my-awesome-counter; 383 | list-style: none; 384 | padding-left:0; 385 | } 386 | ol.list-featured li { 387 | counter-increment: my-awesome-counter; 388 | display: flex; 389 | font-size: 0.8rem; 390 | } 391 | ol.list-featured li:before { 392 | content: "0" counter(my-awesome-counter); 393 | font-weight: bold; 394 | font-size: 3rem; 395 | margin-right: 0.5rem; 396 | font-family: 'Abril Fatface', serif; 397 | line-height: 1; 398 | } 399 | 400 | // Login 401 | 402 | @include media-breakpoint-up(md) { 403 | .loginarea { 404 | z-index: 1111; 405 | } 406 | } 407 | 408 | // Article 409 | article { 410 | font-family: "sans-serif", "Georgia"; 411 | font-size:20px; 412 | line-height:1.7; 413 | p, pre, figure, img, blockquote, iframed, embed { 414 | margin-bottom:2rem; 415 | } 416 | blockquote { 417 | padding-left:40px; 418 | margin-left:0px; 419 | font-style:italic; 420 | position:relative; 421 | } 422 | blockquote:before { 423 | content: "“"; 424 | font-family: Georgia; 425 | font-size: 8rem; 426 | margin: -1rem 2rem 0 -3.9rem; 427 | position: absolute; 428 | opacity: 1; 429 | float: left; 430 | line-height: 1; 431 | } 432 | &:first-letter {float: left; 433 | font-size: 5em;line-height:1; 434 | margin: 0 .2em 0 0;vertical-align: top;} 435 | img { 436 | max-width: 100%; 437 | } 438 | } 439 | 440 | .spanborder { 441 | border-bottom: 1px solid $lightblue; 442 | margin-bottom:2rem; 443 | } 444 | .spanborder span { 445 | border-bottom: 1px solid rgba(0,0,0,.44); 446 | display: inline-block; 447 | padding-bottom: 20px; 448 | margin-bottom: -1px; 449 | } 450 | 451 | .a2a_default_style .a2a_svg { 452 | border-radius:50% !important; 453 | margin-top:.5rem; 454 | margin-left:auto; 455 | margin-right:auto; 456 | } 457 | @include media-breakpoint-down(lg) { 458 | .display-4 {font-size:35px;} 459 | } 460 | @include media-breakpoint-down(md) { 461 | .display-4 {font-size:25px;} 462 | } 463 | @include media-breakpoint-up(lg) { 464 | .a2a_default_style a { 465 | display:block !important; 466 | float:none !important; 467 | } 468 | .a2a_default_style .a2a_svg { 469 | border-radius:50% !important; 470 | display:block !important; 471 | float:none !important; 472 | margin-top:.5rem; 473 | margin-left:auto; 474 | margin-right:auto; 475 | } 476 | .a2a_svg { 477 | height: 40px !important; 478 | line-height: 40px !important; 479 | width: 40px !important; 480 | } 481 | } 482 | // Footer 483 | footer.bg-black { 484 | color: $gray-600; 485 | a { 486 | color: $gray-600; 487 | &:hover { 488 | text-decoration:none; 489 | color: $white; 490 | } 491 | } 492 | } 493 | 494 | .article-post { 495 | img { 496 | max-width: 100%; 497 | } 498 | blockquote { 499 | white-space: break-spaces; 500 | } 501 | } 502 | -------------------------------------------------------------------------------- /schemas/contentful.json: -------------------------------------------------------------------------------- 1 | { 2 | "contentTypes": [ 3 | { 4 | "sys": { 5 | "space": { 6 | "sys": { 7 | "type": "Link", 8 | "linkType": "Space", 9 | "id": "el2orhksvy4m" 10 | } 11 | }, 12 | "id": "category", 13 | "type": "ContentType", 14 | "createdAt": "2020-04-19T21:28:03.385Z", 15 | "updatedAt": "2020-04-19T21:30:18.049Z", 16 | "environment": { 17 | "sys": { 18 | "id": "master", 19 | "type": "Link", 20 | "linkType": "Environment" 21 | } 22 | }, 23 | "publishedVersion": 2, 24 | "publishedAt": "2020-04-19T21:30:18.049Z", 25 | "firstPublishedAt": "2020-04-19T21:30:18.049Z", 26 | "createdBy": { 27 | "sys": { 28 | "type": "Link", 29 | "linkType": "User", 30 | "id": "2b9r06bKrCs1SX7eGWjrer" 31 | } 32 | }, 33 | "updatedBy": { 34 | "sys": { 35 | "type": "Link", 36 | "linkType": "User", 37 | "id": "2b9r06bKrCs1SX7eGWjrer" 38 | } 39 | }, 40 | "publishedCounter": 1, 41 | "version": 3, 42 | "publishedBy": { 43 | "sys": { 44 | "type": "Link", 45 | "linkType": "User", 46 | "id": "2b9r06bKrCs1SX7eGWjrer" 47 | } 48 | } 49 | }, 50 | "displayField": "name", 51 | "name": "Category", 52 | "description": "", 53 | "fields": [ 54 | { 55 | "id": "name", 56 | "name": "name", 57 | "type": "Symbol", 58 | "localized": false, 59 | "required": true, 60 | "validations": [ 61 | ], 62 | "disabled": false, 63 | "omitted": false 64 | }, 65 | { 66 | "id": "slug", 67 | "name": "slug", 68 | "type": "Symbol", 69 | "localized": false, 70 | "required": true, 71 | "validations": [ 72 | { 73 | "unique": true 74 | } 75 | ], 76 | "disabled": false, 77 | "omitted": false 78 | } 79 | ] 80 | }, 81 | { 82 | "sys": { 83 | "space": { 84 | "sys": { 85 | "type": "Link", 86 | "linkType": "Space", 87 | "id": "el2orhksvy4m" 88 | } 89 | }, 90 | "id": "author", 91 | "type": "ContentType", 92 | "createdAt": "2020-04-19T21:31:15.244Z", 93 | "updatedAt": "2020-04-19T21:31:15.761Z", 94 | "environment": { 95 | "sys": { 96 | "id": "master", 97 | "type": "Link", 98 | "linkType": "Environment" 99 | } 100 | }, 101 | "publishedVersion": 1, 102 | "publishedAt": "2020-04-19T21:31:15.761Z", 103 | "firstPublishedAt": "2020-04-19T21:31:15.761Z", 104 | "createdBy": { 105 | "sys": { 106 | "type": "Link", 107 | "linkType": "User", 108 | "id": "2b9r06bKrCs1SX7eGWjrer" 109 | } 110 | }, 111 | "updatedBy": { 112 | "sys": { 113 | "type": "Link", 114 | "linkType": "User", 115 | "id": "2b9r06bKrCs1SX7eGWjrer" 116 | } 117 | }, 118 | "publishedCounter": 1, 119 | "version": 2, 120 | "publishedBy": { 121 | "sys": { 122 | "type": "Link", 123 | "linkType": "User", 124 | "id": "2b9r06bKrCs1SX7eGWjrer" 125 | } 126 | } 127 | }, 128 | "displayField": "name", 129 | "name": "Author", 130 | "description": "", 131 | "fields": [ 132 | { 133 | "id": "name", 134 | "name": "name", 135 | "type": "Symbol", 136 | "localized": false, 137 | "required": false, 138 | "validations": [ 139 | ], 140 | "disabled": false, 141 | "omitted": false 142 | }, 143 | { 144 | "id": "photo", 145 | "name": "photo", 146 | "type": "Link", 147 | "localized": false, 148 | "required": false, 149 | "validations": [ 150 | ], 151 | "disabled": false, 152 | "omitted": false, 153 | "linkType": "Asset" 154 | } 155 | ] 156 | }, 157 | { 158 | "sys": { 159 | "space": { 160 | "sys": { 161 | "type": "Link", 162 | "linkType": "Space", 163 | "id": "el2orhksvy4m" 164 | } 165 | }, 166 | "id": "post", 167 | "type": "ContentType", 168 | "createdAt": "2020-04-19T21:32:18.581Z", 169 | "updatedAt": "2020-04-19T21:32:19.055Z", 170 | "environment": { 171 | "sys": { 172 | "id": "master", 173 | "type": "Link", 174 | "linkType": "Environment" 175 | } 176 | }, 177 | "publishedVersion": 1, 178 | "publishedAt": "2020-04-19T21:32:19.055Z", 179 | "firstPublishedAt": "2020-04-19T21:32:19.055Z", 180 | "createdBy": { 181 | "sys": { 182 | "type": "Link", 183 | "linkType": "User", 184 | "id": "2b9r06bKrCs1SX7eGWjrer" 185 | } 186 | }, 187 | "updatedBy": { 188 | "sys": { 189 | "type": "Link", 190 | "linkType": "User", 191 | "id": "2b9r06bKrCs1SX7eGWjrer" 192 | } 193 | }, 194 | "publishedCounter": 1, 195 | "version": 2, 196 | "publishedBy": { 197 | "sys": { 198 | "type": "Link", 199 | "linkType": "User", 200 | "id": "2b9r06bKrCs1SX7eGWjrer" 201 | } 202 | } 203 | }, 204 | "displayField": "title", 205 | "name": "Post", 206 | "description": "", 207 | "fields": [ 208 | { 209 | "id": "title", 210 | "name": "title", 211 | "type": "Symbol", 212 | "localized": false, 213 | "required": true, 214 | "validations": [ 215 | ], 216 | "disabled": false, 217 | "omitted": false 218 | }, 219 | { 220 | "id": "slug", 221 | "name": "slug", 222 | "type": "Symbol", 223 | "localized": false, 224 | "required": true, 225 | "validations": [ 226 | { 227 | "unique": true 228 | } 229 | ], 230 | "disabled": false, 231 | "omitted": false 232 | }, 233 | { 234 | "id": "hero", 235 | "name": "hero", 236 | "type": "Link", 237 | "localized": false, 238 | "required": false, 239 | "validations": [ 240 | ], 241 | "disabled": false, 242 | "omitted": false, 243 | "linkType": "Asset" 244 | }, 245 | { 246 | "id": "shortBody", 247 | "name": "short body", 248 | "type": "Text", 249 | "localized": false, 250 | "required": true, 251 | "validations": [ 252 | ], 253 | "disabled": false, 254 | "omitted": false 255 | }, 256 | { 257 | "id": "body", 258 | "name": "body", 259 | "type": "Text", 260 | "localized": false, 261 | "required": true, 262 | "validations": [ 263 | ], 264 | "disabled": false, 265 | "omitted": false 266 | }, 267 | { 268 | "id": "publishedAt", 269 | "name": "published at", 270 | "type": "Date", 271 | "localized": false, 272 | "required": true, 273 | "validations": [ 274 | ], 275 | "disabled": false, 276 | "omitted": false 277 | }, 278 | { 279 | "id": "author", 280 | "name": "author", 281 | "type": "Link", 282 | "localized": false, 283 | "required": false, 284 | "validations": [ 285 | ], 286 | "disabled": false, 287 | "omitted": false, 288 | "linkType": "Entry" 289 | }, 290 | { 291 | "id": "category", 292 | "name": "category", 293 | "type": "Link", 294 | "localized": false, 295 | "required": false, 296 | "validations": [ 297 | ], 298 | "disabled": false, 299 | "omitted": false, 300 | "linkType": "Entry" 301 | } 302 | ] 303 | } 304 | ], 305 | "editorInterfaces": [ 306 | { 307 | "sys": { 308 | "id": "default", 309 | "type": "EditorInterface", 310 | "space": { 311 | "sys": { 312 | "id": "el2orhksvy4m", 313 | "type": "Link", 314 | "linkType": "Space" 315 | } 316 | }, 317 | "version": 2, 318 | "createdAt": "2020-04-19T21:30:18.105Z", 319 | "createdBy": { 320 | "sys": { 321 | "id": "2b9r06bKrCs1SX7eGWjrer", 322 | "type": "Link", 323 | "linkType": "User" 324 | } 325 | }, 326 | "updatedAt": "2020-04-19T21:30:18.986Z", 327 | "updatedBy": { 328 | "sys": { 329 | "id": "2b9r06bKrCs1SX7eGWjrer", 330 | "type": "Link", 331 | "linkType": "User" 332 | } 333 | }, 334 | "contentType": { 335 | "sys": { 336 | "id": "category", 337 | "type": "Link", 338 | "linkType": "ContentType" 339 | } 340 | }, 341 | "environment": { 342 | "sys": { 343 | "id": "master", 344 | "type": "Link", 345 | "linkType": "Environment" 346 | } 347 | } 348 | }, 349 | "controls": [ 350 | { 351 | "fieldId": "name", 352 | "widgetId": "singleLine", 353 | "widgetNamespace": "builtin" 354 | }, 355 | { 356 | "fieldId": "slug", 357 | "widgetId": "singleLine", 358 | "widgetNamespace": "builtin" 359 | } 360 | ] 361 | }, 362 | { 363 | "sys": { 364 | "id": "default", 365 | "type": "EditorInterface", 366 | "space": { 367 | "sys": { 368 | "id": "el2orhksvy4m", 369 | "type": "Link", 370 | "linkType": "Space" 371 | } 372 | }, 373 | "version": 2, 374 | "createdAt": "2020-04-19T21:31:15.818Z", 375 | "createdBy": { 376 | "sys": { 377 | "id": "2b9r06bKrCs1SX7eGWjrer", 378 | "type": "Link", 379 | "linkType": "User" 380 | } 381 | }, 382 | "updatedAt": "2020-04-19T21:31:16.112Z", 383 | "updatedBy": { 384 | "sys": { 385 | "id": "2b9r06bKrCs1SX7eGWjrer", 386 | "type": "Link", 387 | "linkType": "User" 388 | } 389 | }, 390 | "contentType": { 391 | "sys": { 392 | "id": "author", 393 | "type": "Link", 394 | "linkType": "ContentType" 395 | } 396 | }, 397 | "environment": { 398 | "sys": { 399 | "id": "master", 400 | "type": "Link", 401 | "linkType": "Environment" 402 | } 403 | } 404 | }, 405 | "controls": [ 406 | { 407 | "fieldId": "name", 408 | "widgetId": "singleLine", 409 | "widgetNamespace": "builtin" 410 | }, 411 | { 412 | "fieldId": "photo", 413 | "widgetId": "assetLinkEditor", 414 | "widgetNamespace": "builtin" 415 | } 416 | ] 417 | }, 418 | { 419 | "sys": { 420 | "id": "default", 421 | "type": "EditorInterface", 422 | "space": { 423 | "sys": { 424 | "id": "el2orhksvy4m", 425 | "type": "Link", 426 | "linkType": "Space" 427 | } 428 | }, 429 | "version": 2, 430 | "createdAt": "2020-04-19T21:32:19.111Z", 431 | "createdBy": { 432 | "sys": { 433 | "id": "2b9r06bKrCs1SX7eGWjrer", 434 | "type": "Link", 435 | "linkType": "User" 436 | } 437 | }, 438 | "updatedAt": "2020-04-19T21:32:19.559Z", 439 | "updatedBy": { 440 | "sys": { 441 | "id": "2b9r06bKrCs1SX7eGWjrer", 442 | "type": "Link", 443 | "linkType": "User" 444 | } 445 | }, 446 | "contentType": { 447 | "sys": { 448 | "id": "post", 449 | "type": "Link", 450 | "linkType": "ContentType" 451 | } 452 | }, 453 | "environment": { 454 | "sys": { 455 | "id": "master", 456 | "type": "Link", 457 | "linkType": "Environment" 458 | } 459 | } 460 | }, 461 | "controls": [ 462 | { 463 | "fieldId": "title", 464 | "widgetId": "singleLine", 465 | "widgetNamespace": "builtin" 466 | }, 467 | { 468 | "fieldId": "slug", 469 | "widgetId": "singleLine", 470 | "widgetNamespace": "builtin" 471 | }, 472 | { 473 | "fieldId": "hero", 474 | "widgetId": "assetLinkEditor", 475 | "widgetNamespace": "builtin" 476 | }, 477 | { 478 | "fieldId": "shortBody", 479 | "widgetId": "markdown", 480 | "widgetNamespace": "builtin" 481 | }, 482 | { 483 | "fieldId": "body", 484 | "widgetId": "markdown", 485 | "widgetNamespace": "builtin" 486 | }, 487 | { 488 | "fieldId": "publishedAt", 489 | "settings": { 490 | "ampm": "24", 491 | "format": "timeZ" 492 | }, 493 | "widgetId": "datePicker", 494 | "widgetNamespace": "builtin" 495 | }, 496 | { 497 | "fieldId": "author", 498 | "widgetId": "entryLinkEditor", 499 | "widgetNamespace": "builtin" 500 | }, 501 | { 502 | "fieldId": "category", 503 | "widgetId": "entryLinkEditor", 504 | "widgetNamespace": "builtin" 505 | } 506 | ] 507 | } 508 | ], 509 | "entries": [ 510 | { 511 | "sys": { 512 | "space": { 513 | "sys": { 514 | "type": "Link", 515 | "linkType": "Space", 516 | "id": "el2orhksvy4m" 517 | } 518 | }, 519 | "id": "4MUpPfHcL6acD902cJSScK", 520 | "type": "Entry", 521 | "createdAt": "2020-04-19T21:30:28.769Z", 522 | "updatedAt": "2020-04-19T21:30:57.596Z", 523 | "environment": { 524 | "sys": { 525 | "id": "master", 526 | "type": "Link", 527 | "linkType": "Environment" 528 | } 529 | }, 530 | "publishedVersion": 7, 531 | "publishedAt": "2020-04-19T21:30:57.596Z", 532 | "firstPublishedAt": "2020-04-19T21:30:57.596Z", 533 | "createdBy": { 534 | "sys": { 535 | "type": "Link", 536 | "linkType": "User", 537 | "id": "2b9r06bKrCs1SX7eGWjrer" 538 | } 539 | }, 540 | "updatedBy": { 541 | "sys": { 542 | "type": "Link", 543 | "linkType": "User", 544 | "id": "2b9r06bKrCs1SX7eGWjrer" 545 | } 546 | }, 547 | "publishedCounter": 1, 548 | "version": 8, 549 | "publishedBy": { 550 | "sys": { 551 | "type": "Link", 552 | "linkType": "User", 553 | "id": "2b9r06bKrCs1SX7eGWjrer" 554 | } 555 | }, 556 | "contentType": { 557 | "sys": { 558 | "type": "Link", 559 | "linkType": "ContentType", 560 | "id": "category" 561 | } 562 | } 563 | }, 564 | "fields": { 565 | "name": { 566 | "en-US": "General" 567 | }, 568 | "slug": { 569 | "en-US": "general" 570 | } 571 | } 572 | }, 573 | { 574 | "sys": { 575 | "space": { 576 | "sys": { 577 | "type": "Link", 578 | "linkType": "Space", 579 | "id": "el2orhksvy4m" 580 | } 581 | }, 582 | "id": "KI6XqoKVPYqWId0lZzNFh", 583 | "type": "Entry", 584 | "createdAt": "2020-04-19T21:31:30.875Z", 585 | "updatedAt": "2020-04-19T21:31:40.551Z", 586 | "environment": { 587 | "sys": { 588 | "id": "master", 589 | "type": "Link", 590 | "linkType": "Environment" 591 | } 592 | }, 593 | "publishedVersion": 7, 594 | "publishedAt": "2020-04-19T21:31:40.551Z", 595 | "firstPublishedAt": "2020-04-19T21:31:40.551Z", 596 | "createdBy": { 597 | "sys": { 598 | "type": "Link", 599 | "linkType": "User", 600 | "id": "2b9r06bKrCs1SX7eGWjrer" 601 | } 602 | }, 603 | "updatedBy": { 604 | "sys": { 605 | "type": "Link", 606 | "linkType": "User", 607 | "id": "2b9r06bKrCs1SX7eGWjrer" 608 | } 609 | }, 610 | "publishedCounter": 1, 611 | "version": 8, 612 | "publishedBy": { 613 | "sys": { 614 | "type": "Link", 615 | "linkType": "User", 616 | "id": "2b9r06bKrCs1SX7eGWjrer" 617 | } 618 | }, 619 | "contentType": { 620 | "sys": { 621 | "type": "Link", 622 | "linkType": "ContentType", 623 | "id": "author" 624 | } 625 | } 626 | }, 627 | "fields": { 628 | "name": { 629 | "en-US": "Maxi Gimenez" 630 | }, 631 | "photo": { 632 | "en-US": { 633 | "sys": { 634 | "type": "Link", 635 | "linkType": "Asset", 636 | "id": "2xts3o136ESaFyjcRkNjIV" 637 | } 638 | } 639 | } 640 | } 641 | }, 642 | { 643 | "sys": { 644 | "space": { 645 | "sys": { 646 | "type": "Link", 647 | "linkType": "Space", 648 | "id": "el2orhksvy4m" 649 | } 650 | }, 651 | "id": "5Bk4Zi2qQJKocz5j2iSFzS", 652 | "type": "Entry", 653 | "createdAt": "2020-04-19T21:33:14.300Z", 654 | "updatedAt": "2020-04-19T21:33:49.891Z", 655 | "environment": { 656 | "sys": { 657 | "id": "master", 658 | "type": "Link", 659 | "linkType": "Environment" 660 | } 661 | }, 662 | "publishedVersion": 10, 663 | "publishedAt": "2020-04-19T21:33:49.891Z", 664 | "firstPublishedAt": "2020-04-19T21:33:49.891Z", 665 | "createdBy": { 666 | "sys": { 667 | "type": "Link", 668 | "linkType": "User", 669 | "id": "2b9r06bKrCs1SX7eGWjrer" 670 | } 671 | }, 672 | "updatedBy": { 673 | "sys": { 674 | "type": "Link", 675 | "linkType": "User", 676 | "id": "2b9r06bKrCs1SX7eGWjrer" 677 | } 678 | }, 679 | "publishedCounter": 1, 680 | "version": 11, 681 | "publishedBy": { 682 | "sys": { 683 | "type": "Link", 684 | "linkType": "User", 685 | "id": "2b9r06bKrCs1SX7eGWjrer" 686 | } 687 | }, 688 | "contentType": { 689 | "sys": { 690 | "type": "Link", 691 | "linkType": "ContentType", 692 | "id": "post" 693 | } 694 | } 695 | }, 696 | "fields": { 697 | "title": { 698 | "en-US": "Other cool post" 699 | }, 700 | "slug": { 701 | "en-US": "other-cool-post" 702 | }, 703 | "hero": { 704 | "en-US": { 705 | "sys": { 706 | "type": "Link", 707 | "linkType": "Asset", 708 | "id": "5iUDnHbf75bbBUNj9YS0zh" 709 | } 710 | } 711 | }, 712 | "shortBody": { 713 | "en-US": "__Short description__ for the new blog post, to try how it looks with less content." 714 | }, 715 | "body": { 716 | "en-US": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of __Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.__\n\nLorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to *make a type specimen book. It has survived not only five centuries*, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.\n\n\n\nLorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." 717 | }, 718 | "publishedAt": { 719 | "en-US": "2020-04-02T00:00+02:00" 720 | }, 721 | "author": { 722 | "en-US": { 723 | "sys": { 724 | "type": "Link", 725 | "linkType": "Entry", 726 | "id": "KI6XqoKVPYqWId0lZzNFh" 727 | } 728 | } 729 | }, 730 | "category": { 731 | "en-US": { 732 | "sys": { 733 | "type": "Link", 734 | "linkType": "Entry", 735 | "id": "4MUpPfHcL6acD902cJSScK" 736 | } 737 | } 738 | } 739 | } 740 | }, 741 | { 742 | "sys": { 743 | "space": { 744 | "sys": { 745 | "type": "Link", 746 | "linkType": "Space", 747 | "id": "el2orhksvy4m" 748 | } 749 | }, 750 | "id": "35QlNLR7b0k2SUNtX19fYO", 751 | "type": "Entry", 752 | "createdAt": "2020-04-19T21:34:11.388Z", 753 | "updatedAt": "2020-04-19T21:34:39.333Z", 754 | "environment": { 755 | "sys": { 756 | "id": "master", 757 | "type": "Link", 758 | "linkType": "Environment" 759 | } 760 | }, 761 | "publishedVersion": 11, 762 | "publishedAt": "2020-04-19T21:34:39.333Z", 763 | "firstPublishedAt": "2020-04-19T21:34:39.333Z", 764 | "createdBy": { 765 | "sys": { 766 | "type": "Link", 767 | "linkType": "User", 768 | "id": "2b9r06bKrCs1SX7eGWjrer" 769 | } 770 | }, 771 | "updatedBy": { 772 | "sys": { 773 | "type": "Link", 774 | "linkType": "User", 775 | "id": "2b9r06bKrCs1SX7eGWjrer" 776 | } 777 | }, 778 | "publishedCounter": 1, 779 | "version": 12, 780 | "publishedBy": { 781 | "sys": { 782 | "type": "Link", 783 | "linkType": "User", 784 | "id": "2b9r06bKrCs1SX7eGWjrer" 785 | } 786 | }, 787 | "contentType": { 788 | "sys": { 789 | "type": "Link", 790 | "linkType": "ContentType", 791 | "id": "post" 792 | } 793 | } 794 | }, 795 | "fields": { 796 | "title": { 797 | "en-US": "Next.js + Contentful + Medium style blog" 798 | }, 799 | "slug": { 800 | "en-US": "nextjs-contentful-medium" 801 | }, 802 | "hero": { 803 | "en-US": { 804 | "sys": { 805 | "type": "Link", 806 | "linkType": "Asset", 807 | "id": "1nf8ONgIhGEu1Ybo1d4PnH" 808 | } 809 | } 810 | }, 811 | "shortBody": { 812 | "en-US": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book." 813 | }, 814 | "body": { 815 | "en-US": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.\n\n> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.\n\nLorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." 816 | }, 817 | "publishedAt": { 818 | "en-US": "2020-04-19T00:00+02:00" 819 | }, 820 | "author": { 821 | "en-US": { 822 | "sys": { 823 | "type": "Link", 824 | "linkType": "Entry", 825 | "id": "KI6XqoKVPYqWId0lZzNFh" 826 | } 827 | } 828 | }, 829 | "category": { 830 | "en-US": { 831 | "sys": { 832 | "type": "Link", 833 | "linkType": "Entry", 834 | "id": "4MUpPfHcL6acD902cJSScK" 835 | } 836 | } 837 | } 838 | } 839 | } 840 | ], 841 | "assets": [ 842 | { 843 | "sys": { 844 | "space": { 845 | "sys": { 846 | "type": "Link", 847 | "linkType": "Space", 848 | "id": "el2orhksvy4m" 849 | } 850 | }, 851 | "id": "2xts3o136ESaFyjcRkNjIV", 852 | "type": "Asset", 853 | "createdAt": "2020-04-19T21:24:18.621Z", 854 | "updatedAt": "2020-04-19T21:24:28.716Z", 855 | "environment": { 856 | "sys": { 857 | "id": "master", 858 | "type": "Link", 859 | "linkType": "Environment" 860 | } 861 | }, 862 | "publishedVersion": 2, 863 | "publishedAt": "2020-04-19T21:24:28.716Z", 864 | "firstPublishedAt": "2020-04-19T21:24:28.716Z", 865 | "createdBy": { 866 | "sys": { 867 | "type": "Link", 868 | "linkType": "User", 869 | "id": "2b9r06bKrCs1SX7eGWjrer" 870 | } 871 | }, 872 | "updatedBy": { 873 | "sys": { 874 | "type": "Link", 875 | "linkType": "User", 876 | "id": "2b9r06bKrCs1SX7eGWjrer" 877 | } 878 | }, 879 | "publishedCounter": 1, 880 | "version": 3, 881 | "publishedBy": { 882 | "sys": { 883 | "type": "Link", 884 | "linkType": "User", 885 | "id": "2b9r06bKrCs1SX7eGWjrer" 886 | } 887 | } 888 | }, 889 | "fields": { 890 | "title": { 891 | "en-US": "Maxi Gimenez" 892 | }, 893 | "file": { 894 | "en-US": { 895 | "url": "//images.ctfassets.net/el2orhksvy4m/2xts3o136ESaFyjcRkNjIV/10ee6cbee2257d9b1a28f8bc7690026c/IMG_2404-min.jpg", 896 | "details": { 897 | "size": 500396, 898 | "image": { 899 | "width": 2283, 900 | "height": 2583 901 | } 902 | }, 903 | "fileName": "IMG_2404-min.jpg", 904 | "contentType": "image/jpeg" 905 | } 906 | } 907 | } 908 | }, 909 | { 910 | "sys": { 911 | "space": { 912 | "sys": { 913 | "type": "Link", 914 | "linkType": "Space", 915 | "id": "el2orhksvy4m" 916 | } 917 | }, 918 | "id": "1nf8ONgIhGEu1Ybo1d4PnH", 919 | "type": "Asset", 920 | "createdAt": "2020-04-19T21:24:18.825Z", 921 | "updatedAt": "2020-04-19T21:24:29.143Z", 922 | "environment": { 923 | "sys": { 924 | "id": "master", 925 | "type": "Link", 926 | "linkType": "Environment" 927 | } 928 | }, 929 | "publishedVersion": 2, 930 | "publishedAt": "2020-04-19T21:24:29.143Z", 931 | "firstPublishedAt": "2020-04-19T21:24:29.143Z", 932 | "createdBy": { 933 | "sys": { 934 | "type": "Link", 935 | "linkType": "User", 936 | "id": "2b9r06bKrCs1SX7eGWjrer" 937 | } 938 | }, 939 | "updatedBy": { 940 | "sys": { 941 | "type": "Link", 942 | "linkType": "User", 943 | "id": "2b9r06bKrCs1SX7eGWjrer" 944 | } 945 | }, 946 | "publishedCounter": 1, 947 | "version": 3, 948 | "publishedBy": { 949 | "sys": { 950 | "type": "Link", 951 | "linkType": "User", 952 | "id": "2b9r06bKrCs1SX7eGWjrer" 953 | } 954 | } 955 | }, 956 | "fields": { 957 | "title": { 958 | "en-US": "Moving" 959 | }, 960 | "description": { 961 | "en-US": "Photo by Marc-Olivier Jodoin on Unsplash" 962 | }, 963 | "file": { 964 | "en-US": { 965 | "url": "//images.ctfassets.net/el2orhksvy4m/1nf8ONgIhGEu1Ybo1d4PnH/90e8c1f4f4e0ddb3ec10323715d49548/marc-olivier-jodoin-NqOInJ-ttqM-unsplash__1_.jpg", 966 | "details": { 967 | "size": 252046, 968 | "image": { 969 | "width": 2000, 970 | "height": 1125 971 | } 972 | }, 973 | "fileName": "marc-olivier-jodoin-NqOInJ-ttqM-unsplash (1).jpg", 974 | "contentType": "image/jpeg" 975 | } 976 | } 977 | } 978 | }, 979 | { 980 | "sys": { 981 | "space": { 982 | "sys": { 983 | "type": "Link", 984 | "linkType": "Space", 985 | "id": "el2orhksvy4m" 986 | } 987 | }, 988 | "id": "5iUDnHbf75bbBUNj9YS0zh", 989 | "type": "Asset", 990 | "createdAt": "2020-04-19T21:24:19.106Z", 991 | "updatedAt": "2020-04-19T21:24:28.172Z", 992 | "environment": { 993 | "sys": { 994 | "id": "master", 995 | "type": "Link", 996 | "linkType": "Environment" 997 | } 998 | }, 999 | "publishedVersion": 2, 1000 | "publishedAt": "2020-04-19T21:24:28.172Z", 1001 | "firstPublishedAt": "2020-04-19T21:24:28.172Z", 1002 | "createdBy": { 1003 | "sys": { 1004 | "type": "Link", 1005 | "linkType": "User", 1006 | "id": "2b9r06bKrCs1SX7eGWjrer" 1007 | } 1008 | }, 1009 | "updatedBy": { 1010 | "sys": { 1011 | "type": "Link", 1012 | "linkType": "User", 1013 | "id": "2b9r06bKrCs1SX7eGWjrer" 1014 | } 1015 | }, 1016 | "publishedCounter": 1, 1017 | "version": 3, 1018 | "publishedBy": { 1019 | "sys": { 1020 | "type": "Link", 1021 | "linkType": "User", 1022 | "id": "2b9r06bKrCs1SX7eGWjrer" 1023 | } 1024 | } 1025 | }, 1026 | "fields": { 1027 | "title": { 1028 | "en-US": "photo-1506775352297-a5fa9c136675" 1029 | }, 1030 | "file": { 1031 | "en-US": { 1032 | "url": "//images.ctfassets.net/el2orhksvy4m/5iUDnHbf75bbBUNj9YS0zh/49006819d437fedf1a840253571ab4c8/photo-1506775352297-a5fa9c136675", 1033 | "details": { 1034 | "size": 113088, 1035 | "image": { 1036 | "width": 1489, 1037 | "height": 838 1038 | } 1039 | }, 1040 | "fileName": "photo-1506775352297-a5fa9c136675", 1041 | "contentType": "image/jpeg" 1042 | } 1043 | } 1044 | } 1045 | } 1046 | ], 1047 | "locales": [ 1048 | { 1049 | "name": "English (United States)", 1050 | "code": "en-US", 1051 | "fallbackCode": null, 1052 | "default": true, 1053 | "contentManagementApi": true, 1054 | "contentDeliveryApi": true, 1055 | "optional": false, 1056 | "sys": { 1057 | "type": "Locale", 1058 | "id": "5FjdkmToYwbWvINfjT0W6w", 1059 | "version": 1, 1060 | "space": { 1061 | "sys": { 1062 | "type": "Link", 1063 | "linkType": "Space", 1064 | "id": "el2orhksvy4m" 1065 | } 1066 | }, 1067 | "environment": { 1068 | "sys": { 1069 | "type": "Link", 1070 | "linkType": "Environment", 1071 | "id": "master" 1072 | } 1073 | }, 1074 | "createdBy": { 1075 | "sys": { 1076 | "type": "Link", 1077 | "linkType": "User", 1078 | "id": "2b9r06bKrCs1SX7eGWjrer" 1079 | } 1080 | }, 1081 | "createdAt": "2020-04-19T20:53:21Z", 1082 | "updatedBy": { 1083 | "sys": { 1084 | "type": "Link", 1085 | "linkType": "User", 1086 | "id": "2b9r06bKrCs1SX7eGWjrer" 1087 | } 1088 | }, 1089 | "updatedAt": "2020-04-19T20:53:21Z" 1090 | } 1091 | } 1092 | ], 1093 | "webhooks": [ 1094 | ], 1095 | "roles": [ 1096 | ] 1097 | } 1098 | -------------------------------------------------------------------------------- /styles/bootstrap-user-variables.scss: -------------------------------------------------------------------------------- 1 | 2 | // --------------------------------------- !! 3 | // Overwrite specific bootstrap variables by uncommenting the variable and provide your own value 4 | // --------------------------------------- !! 5 | 6 | // Variables 7 | // 8 | // Variables should follow the `$component-state-property-size` formula for 9 | // consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs. 10 | 11 | 12 | // 13 | // Color system 14 | // 15 | 16 | $white: #fff !default; 17 | $gray-100: #f8f9fa !default; 18 | $gray-200: #e9ecef !default; 19 | $gray-300: #dee2e6 !default; 20 | $gray-400: #ced4da !default; 21 | $gray-500: #adb5bd !default; 22 | $gray-600: #6c757d !default; 23 | $gray-700: #495057 !default; 24 | $gray-800: #343a40 !default; 25 | $gray-900: #212529 !default; 26 | $black: #000 !default; 27 | 28 | 29 | $grays: () !default; 30 | // // stylelint-disable-next-line scss/dollar-variable-default 31 | $grays: map-merge( 32 | ( 33 | "100": $gray-100, 34 | "200": $gray-200, 35 | "300": $gray-300, 36 | "400": $gray-400, 37 | "500": $gray-500, 38 | "600": $gray-600, 39 | "700": $gray-700, 40 | "800": $gray-800, 41 | "900": $gray-900 42 | ), 43 | $grays 44 | ); 45 | 46 | 47 | $blue: #7832e2 !default; 48 | $indigo: #502c6c !default; 49 | $purple: #ad6edd !default; 50 | $pink: #ff0266 !default; 51 | $red: #ea2f65 !default; 52 | $orange: #fbb500 !default; 53 | $yellow: #ffde03 !default; 54 | $green: #03a87c !default; 55 | $teal: #09ebaf !default; 56 | $cyan: #35bdff !default; 57 | $lila: #b2a5e4 !default; 58 | $salmon: #ff977a !default; 59 | $lightblue: #e8f3ec !default; 60 | 61 | 62 | $colors: () !default; 63 | // // stylelint-disable-next-line scss/dollar-variable-default 64 | $colors: map-merge( 65 | ( 66 | "blue": $blue, 67 | "indigo": $indigo, 68 | "purple": $purple, 69 | "pink": $pink, 70 | "red": $red, 71 | "orange": $orange, 72 | "yellow": $yellow, 73 | "green": $green, 74 | "teal": $teal, 75 | "cyan": $cyan, 76 | "white": $white, 77 | "gray": $gray-600, 78 | "gray-dark": $gray-800, 79 | "light-blue": $lightblue 80 | ), 81 | $colors 82 | ); 83 | 84 | $primary: $green !default; 85 | $secondary: $blue !default; 86 | $success: $green !default; 87 | $info: $teal !default; 88 | $warning: $yellow !default; 89 | $danger: $red !default; 90 | $light: $gray-100 !default; 91 | $dark: $gray-900 !default; 92 | $white: $white !default; 93 | $lightblue: $lightblue !default; 94 | 95 | 96 | $theme-colors: () !default; 97 | // // stylelint-disable-next-line scss/dollar-variable-default 98 | $theme-colors: map-merge( 99 | ( 100 | "primary": $primary, 101 | "secondary": $secondary, 102 | "success": $success, 103 | "info": $info, 104 | "warning": $warning, 105 | "danger": $danger, 106 | "light": $light, 107 | "dark": $dark, 108 | "white": $white, 109 | "purple": $purple, 110 | "salmon": $salmon, 111 | "cyan": $cyan, 112 | "gray": $gray-400, 113 | "indigo": $indigo, 114 | "orange": $orange, 115 | "lightblue": $lightblue 116 | ), 117 | $theme-colors 118 | ); 119 | // 120 | // // Set a specific jump point for requesting color jumps 121 | // $theme-color-interval: 8% !default; 122 | // 123 | // // The yiq lightness value that determines when the lightness of color changes from "dark" to "light". Acceptable values are between 0 and 255. 124 | // $yiq-contrasted-threshold: 150 !default; 125 | // 126 | // // Customize the light and dark text colors for use in our YIQ color contrast function. 127 | // $yiq-text-dark: $gray-900 !default; 128 | // $yiq-text-light: $white !default; 129 | // 130 | // // Options 131 | // // 132 | // // Quickly modify global styling by enabling or disabling optional features. 133 | // 134 | // $enable-caret: true !default; 135 | // $enable-rounded: true !default; 136 | // $enable-shadows: false !default; 137 | // $enable-gradients: false !default; 138 | // $enable-transitions: true !default; 139 | // $enable-hover-media-query: false !default; // Deprecated, no longer affects any compiled CSS 140 | // $enable-grid-classes: true !default; 141 | // $enable-print-styles: true !default; 142 | // 143 | // 144 | // // Spacing 145 | // // 146 | // // Control the default styling of most Bootstrap elements by modifying these 147 | // // variables. Mostly focused on spacing. 148 | // // You can add more entries to the $spacers map, should you need more variation. 149 | // 150 | $spacer: 1rem !default; 151 | $spacers: () !default; 152 | // // stylelint-disable-next-line scss/dollar-variable-default 153 | $spacers: map-merge( 154 | ( 155 | 0: 0, 156 | 1: ($spacer * .25), 157 | 2: ($spacer * .5), 158 | 3: $spacer, 159 | 4: ($spacer * 1.5), 160 | 5: ($spacer * 3), 161 | 6: ($spacer * 5), 162 | ), 163 | $spacers 164 | ); 165 | // 166 | // // This variable affects the `.h-*` and `.w-*` classes. 167 | // $sizes: () !default; 168 | // // stylelint-disable-next-line scss/dollar-variable-default 169 | // $sizes: map-merge( 170 | // ( 171 | // 25: 25%, 172 | // 50: 50%, 173 | // 75: 75%, 174 | // 100: 100%, 175 | // auto: auto 176 | // ), 177 | // $sizes 178 | // ); 179 | // 180 | // // Body 181 | // // 182 | // // Settings for the `` element. 183 | // 184 | // $body-bg: $white !default; 185 | // $body-color: $gray-900 !default; 186 | // 187 | // // Links 188 | // // 189 | // // Style anchor elements. 190 | // 191 | // $link-color: theme-color("primary") !default; 192 | // $link-decoration: none !default; 193 | // $link-hover-color: darken($link-color, 15%) !default; 194 | // $link-hover-decoration: underline !default; 195 | // 196 | // // Paragraphs 197 | // // 198 | // // Style p element. 199 | // 200 | // $paragraph-margin-bottom: 1rem !default; 201 | // 202 | // 203 | // // Grid breakpoints 204 | // // 205 | // // Define the minimum dimensions at which your layout will change, 206 | // // adapting to different screen sizes, for use in media queries. 207 | // 208 | // $grid-breakpoints: ( 209 | // xs: 0, 210 | // sm: 576px, 211 | // md: 768px, 212 | // lg: 992px, 213 | // xl: 1200px 214 | // ) !default; 215 | // 216 | // @include _assert-ascending($grid-breakpoints, "$grid-breakpoints"); 217 | // @include _assert-starts-at-zero($grid-breakpoints); 218 | // 219 | // 220 | // // Grid containers 221 | // // 222 | // // Define the maximum width of `.container` for different screen sizes. 223 | // 224 | // $container-max-widths: ( 225 | // sm: 540px, 226 | // md: 720px, 227 | // lg: 960px, 228 | // xl: 1140px 229 | // ) !default; 230 | // 231 | // @include _assert-ascending($container-max-widths, "$container-max-widths"); 232 | // 233 | // 234 | // // Grid columns 235 | // // 236 | // // Set the number of columns and specify the width of the gutters. 237 | // 238 | // $grid-columns: 12 !default; 239 | // $grid-gutter-width: 30px !default; 240 | // 241 | // // Components 242 | // // 243 | // // Define common padding and border radius sizes and more. 244 | // 245 | // $line-height-lg: 1.5 !default; 246 | // $line-height-sm: 1.5 !default; 247 | // 248 | // $border-width: 1px !default; 249 | $border-color: $lightblue !default; 250 | // 251 | // $border-radius: .25rem !default; 252 | // $border-radius-lg: .3rem !default; 253 | // $border-radius-sm: .2rem !default; 254 | // 255 | // $box-shadow-sm: 0 .125rem .25rem rgba($black, .075) !default; 256 | // $box-shadow: 0 .5rem 1rem rgba($black, .15) !default; 257 | $box-shadow-lg: 0 10px 25px 0 rgba($black,.3) !default; 258 | // 259 | // $component-active-color: $white !default; 260 | // $component-active-bg: theme-color("primary") !default; 261 | // 262 | // $caret-width: .3em !default; 263 | // 264 | // $transition-base: all .2s ease-in-out !default; 265 | // $transition-fade: opacity .15s linear !default; 266 | // $transition-collapse: height .35s ease !default; 267 | // 268 | // 269 | // // Fonts 270 | // // 271 | // // Font, line-height, and color for body text, headings, and more. 272 | // 273 | // // stylelint-disable value-keyword-case 274 | $font-family-sans-serif: "Source Sans Pro", "Segoe UI", "Helvetica Neue", "Arial" !default; 275 | $font-secondary: "Playfair Display", "Segoe UI", "Helvetica Neue", "Arial" !default; 276 | // $font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default; 277 | // $font-family-base: $font-family-sans-serif !default; 278 | // // stylelint-enable value-keyword-case 279 | // 280 | $font-size-base: 1rem !default; // Assumes the browser default, typically `16px` 281 | $font-size-lg: ($font-size-base * 1.15) !default; 282 | $font-size-smaller: ($font-size-base * 1) !default; 283 | // $font-size-sm: ($font-size-base * .875) !default; 284 | // 285 | $font-weight-light: 300 !default; 286 | $font-weight-normal: 400 !default; 287 | // $font-weight-bold: 700 !default; 288 | // 289 | $font-weight-base: $font-weight-normal !default; 290 | $line-height-base: 1.6 !default; 291 | // 292 | // $h1-font-size: $font-size-base * 2.5 !default; 293 | // $h2-font-size: $font-size-base * 2 !default; 294 | // $h3-font-size: $font-size-base * 1.75 !default; 295 | // $h4-font-size: $font-size-base * 1.5 !default; 296 | // $h5-font-size: $font-size-base * 1.25 !default; 297 | // $h6-font-size: $font-size-base !default; 298 | // 299 | // $headings-margin-bottom: ($spacer / 2) !default; 300 | $headings-font-family: inherit !default; 301 | $headings-font-weight: 500 !default; 302 | $headings-line-height: 1.2 !default; 303 | // $headings-color: inherit !default; 304 | // 305 | // $display1-size: 6rem !default; 306 | // $display2-size: 5.5rem !default; 307 | // $display3-size: 4.5rem !default; 308 | // $display4-size: 3.5rem !default; 309 | // 310 | // $display1-weight: 300 !default; 311 | // $display2-weight: 300 !default; 312 | // $display3-weight: 300 !default; 313 | // $display4-weight: 300 !default; 314 | // $display-line-height: $headings-line-height !default; 315 | // 316 | // $lead-font-size: ($font-size-base * 1.25) !default; 317 | // $lead-font-weight: 300 !default; 318 | // 319 | $small-font-size: 85% !default; 320 | // 321 | // $text-muted: $gray-600 !default; 322 | // 323 | // $blockquote-small-color: $gray-600 !default; 324 | // $blockquote-font-size: ($font-size-base * 1.25) !default; 325 | // 326 | // $hr-border-color: rgba($black, .1) !default; 327 | // $hr-border-width: $border-width !default; 328 | // 329 | // $mark-padding: .2em !default; 330 | // 331 | // $dt-font-weight: $font-weight-bold !default; 332 | // 333 | // $kbd-box-shadow: inset 0 -.1rem 0 rgba($black, .25) !default; 334 | // $nested-kbd-font-weight: $font-weight-bold !default; 335 | // 336 | // $list-inline-padding: .5rem !default; 337 | // 338 | // $mark-bg: #fcf8e3 !default; 339 | // 340 | // $hr-margin-y: $spacer !default; 341 | // 342 | // 343 | // // Tables 344 | // // 345 | // // Customizes the `.table` component with basic values, each used across all table variations. 346 | // 347 | // $table-cell-padding: .75rem !default; 348 | // $table-cell-padding-sm: .3rem !default; 349 | // 350 | // $table-bg: transparent !default; 351 | // $table-accent-bg: rgba($black, .05) !default; 352 | // $table-hover-bg: rgba($black, .075) !default; 353 | // $table-active-bg: $table-hover-bg !default; 354 | // 355 | // $table-border-width: $border-width !default; 356 | // $table-border-color: $gray-300 !default; 357 | // 358 | // $table-head-bg: $gray-200 !default; 359 | // $table-head-color: $gray-700 !default; 360 | // 361 | // $table-dark-bg: $gray-900 !default; 362 | // $table-dark-accent-bg: rgba($white, .05) !default; 363 | // $table-dark-hover-bg: rgba($white, .075) !default; 364 | // $table-dark-border-color: lighten($gray-900, 7.5%) !default; 365 | // $table-dark-color: $body-bg !default; 366 | // 367 | // $table-striped-order: odd !default; 368 | // 369 | // $table-caption-color: $text-muted !default; 370 | // 371 | // // Buttons + Forms 372 | // // 373 | // // Shared variables that are reassigned to `$input-` and `$btn-` specific variables. 374 | // 375 | $input-btn-padding-y: .45rem !default; 376 | $input-btn-padding-x: 1.2rem !default; 377 | // $input-btn-line-height: $line-height-base !default; 378 | // 379 | // $input-btn-focus-width: .2rem !default; 380 | // $input-btn-focus-color: rgba($component-active-bg, .25) !default; 381 | // $input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color !default; 382 | // 383 | // $input-btn-padding-y-sm: .25rem !default; 384 | $input-btn-padding-x-sm: 1rem !default; 385 | // $input-btn-line-height-sm: $line-height-sm !default; 386 | // 387 | $input-btn-padding-y-lg: .65rem !default; 388 | $input-btn-padding-x-lg: 2rem !default; 389 | // $input-btn-line-height-lg: $line-height-lg !default; 390 | // 391 | // $input-btn-border-width: $border-width !default; 392 | // 393 | // 394 | // // Buttons 395 | // // 396 | // // For each of Bootstrap's buttons, define text, background, and border color. 397 | // 398 | // $btn-padding-y: $input-btn-padding-y !default; 399 | // $btn-padding-x: $input-btn-padding-x !default; 400 | // $btn-line-height: $input-btn-line-height !default; 401 | // 402 | // $btn-padding-y-sm: $input-btn-padding-y-sm !default; 403 | // $btn-padding-x-sm: $input-btn-padding-x-sm !default; 404 | // $btn-line-height-sm: $input-btn-line-height-sm !default; 405 | // 406 | // $btn-padding-y-lg: $input-btn-padding-y-lg !default; 407 | // $btn-padding-x-lg: $input-btn-padding-x-lg !default; 408 | // $btn-line-height-lg: $input-btn-line-height-lg !default; 409 | // 410 | // $btn-border-width: $input-btn-border-width !default; 411 | // 412 | // $btn-font-weight: $font-weight-normal !default; 413 | // $btn-box-shadow: inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default; 414 | // $btn-focus-width: $input-btn-focus-width !default; 415 | // $btn-focus-box-shadow: $input-btn-focus-box-shadow !default; 416 | // $btn-disabled-opacity: .65 !default; 417 | // $btn-active-box-shadow: inset 0 3px 5px rgba($black, .125) !default; 418 | // 419 | // $btn-link-disabled-color: $gray-600 !default; 420 | // 421 | // $btn-block-spacing-y: .5rem !default; 422 | // 423 | // // Allows for customizing button radius independently from global border radius 424 | // $btn-border-radius: $border-radius !default; 425 | // $btn-border-radius-lg: $border-radius-lg !default; 426 | // $btn-border-radius-sm: $border-radius-sm !default; 427 | // 428 | // $btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default; 429 | 430 | // 431 | // 432 | // // Forms 433 | // 434 | // $label-margin-bottom: .5rem !default; 435 | // 436 | // $input-padding-y: $input-btn-padding-y !default; 437 | // $input-padding-x: $input-btn-padding-x !default; 438 | // $input-line-height: $input-btn-line-height !default; 439 | // 440 | // $input-padding-y-sm: $input-btn-padding-y-sm !default; 441 | // $input-padding-x-sm: $input-btn-padding-x-sm !default; 442 | // $input-line-height-sm: $input-btn-line-height-sm !default; 443 | // 444 | // $input-padding-y-lg: $input-btn-padding-y-lg !default; 445 | // $input-padding-x-lg: $input-btn-padding-x-lg !default; 446 | // $input-line-height-lg: $input-btn-line-height-lg !default; 447 | // 448 | // $input-bg: $white !default; 449 | // $input-disabled-bg: $gray-200 !default; 450 | // 451 | // $input-color: $gray-700 !default; 452 | // $input-border-color: $gray-400 !default; 453 | // $input-border-width: $input-btn-border-width !default; 454 | // $input-box-shadow: inset 0 1px 1px rgba($black, .075) !default; 455 | // 456 | // $input-border-radius: $border-radius !default; 457 | // $input-border-radius-lg: $border-radius-lg !default; 458 | // $input-border-radius-sm: $border-radius-sm !default; 459 | // 460 | // $input-focus-bg: $input-bg !default; 461 | // $input-focus-border-color: lighten($component-active-bg, 25%) !default; 462 | // $input-focus-color: $input-color !default; 463 | // $input-focus-width: $input-btn-focus-width !default; 464 | // $input-focus-box-shadow: $input-btn-focus-box-shadow !default; 465 | // 466 | // $input-placeholder-color: $gray-600 !default; 467 | // $input-plaintext-color: $body-color !default; 468 | // 469 | // $input-height-border: $input-border-width * 2 !default; 470 | // 471 | // $input-height-inner: ($font-size-base * $input-btn-line-height) + ($input-btn-padding-y * 2) !default; 472 | // $input-height: calc(#{$input-height-inner} + #{$input-height-border}) !default; 473 | // 474 | // $input-height-inner-sm: ($font-size-sm * $input-btn-line-height-sm) + ($input-btn-padding-y-sm * 2) !default; 475 | // $input-height-sm: calc(#{$input-height-inner-sm} + #{$input-height-border}) !default; 476 | // 477 | // $input-height-inner-lg: ($font-size-lg * $input-btn-line-height-lg) + ($input-btn-padding-y-lg * 2) !default; 478 | // $input-height-lg: calc(#{$input-height-inner-lg} + #{$input-height-border}) !default; 479 | // 480 | // $input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default; 481 | // 482 | // $form-text-margin-top: .25rem !default; 483 | // 484 | // $form-check-input-gutter: 1.25rem !default; 485 | // $form-check-input-margin-y: .3rem !default; 486 | // $form-check-input-margin-x: .25rem !default; 487 | // 488 | // $form-check-inline-margin-x: .75rem !default; 489 | // $form-check-inline-input-margin-x: .3125rem !default; 490 | // 491 | // $form-group-margin-bottom: 1rem !default; 492 | // 493 | // $input-group-addon-color: $input-color !default; 494 | // $input-group-addon-bg: $gray-200 !default; 495 | // $input-group-addon-border-color: $input-border-color !default; 496 | // 497 | // $custom-forms-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default; 498 | // 499 | // $custom-control-gutter: 1.5rem !default; 500 | // $custom-control-spacer-x: 1rem !default; 501 | // 502 | // $custom-control-indicator-size: 1rem !default; 503 | // $custom-control-indicator-bg: $gray-300 !default; 504 | // $custom-control-indicator-bg-size: 50% 50% !default; 505 | // $custom-control-indicator-box-shadow: inset 0 .25rem .25rem rgba($black, .1) !default; 506 | // 507 | // $custom-control-indicator-disabled-bg: $gray-200 !default; 508 | // $custom-control-label-disabled-color: $gray-600 !default; 509 | // 510 | // $custom-control-indicator-checked-color: $component-active-color !default; 511 | // $custom-control-indicator-checked-bg: $component-active-bg !default; 512 | // $custom-control-indicator-checked-disabled-bg: rgba(theme-color("primary"), .5) !default; 513 | // $custom-control-indicator-checked-box-shadow: none !default; 514 | // 515 | // $custom-control-indicator-focus-box-shadow: 0 0 0 1px $body-bg, $input-btn-focus-box-shadow !default; 516 | // 517 | // $custom-control-indicator-active-color: $component-active-color !default; 518 | // $custom-control-indicator-active-bg: lighten($component-active-bg, 35%) !default; 519 | // $custom-control-indicator-active-box-shadow: none !default; 520 | // 521 | // $custom-checkbox-indicator-border-radius: $border-radius !default; 522 | // $custom-checkbox-indicator-icon-checked: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$custom-control-indicator-checked-color}' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"), "#", "%23") !default; 523 | // 524 | // $custom-checkbox-indicator-indeterminate-bg: $component-active-bg !default; 525 | // $custom-checkbox-indicator-indeterminate-color: $custom-control-indicator-checked-color !default; 526 | // $custom-checkbox-indicator-icon-indeterminate: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='#{$custom-checkbox-indicator-indeterminate-color}' d='M0 2h4'/%3E%3C/svg%3E"), "#", "%23") !default; 527 | // $custom-checkbox-indicator-indeterminate-box-shadow: none !default; 528 | // 529 | // $custom-radio-indicator-border-radius: 50% !default; 530 | // $custom-radio-indicator-icon-checked: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='#{$custom-control-indicator-checked-color}'/%3E%3C/svg%3E"), "#", "%23") !default; 531 | // 532 | // $custom-select-padding-y: .375rem !default; 533 | // $custom-select-padding-x: .75rem !default; 534 | // $custom-select-height: $input-height !default; 535 | // $custom-select-indicator-padding: 1rem !default; // Extra padding to account for the presence of the background-image based indicator 536 | // $custom-select-line-height: $input-btn-line-height !default; 537 | // $custom-select-color: $input-color !default; 538 | // $custom-select-disabled-color: $gray-600 !default; 539 | // $custom-select-bg: $input-bg !default; 540 | // $custom-select-disabled-bg: $gray-200 !default; 541 | // $custom-select-bg-size: 8px 10px !default; // In pixels because image dimensions 542 | // $custom-select-indicator-color: $gray-800 !default; 543 | // $custom-select-indicator: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='#{$custom-select-indicator-color}' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E"), "#", "%23") !default; 544 | // $custom-select-border-width: $input-btn-border-width !default; 545 | // $custom-select-border-color: $input-border-color !default; 546 | // $custom-select-border-radius: $border-radius !default; 547 | // $custom-select-box-shadow: inset 0 1px 2px rgba($black, .075) !default; 548 | // 549 | // $custom-select-focus-border-color: $input-focus-border-color !default; 550 | // $custom-select-focus-width: $input-btn-focus-width !default; 551 | // $custom-select-focus-box-shadow: 0 0 0 $custom-select-focus-width rgba($custom-select-focus-border-color, .5) !default; 552 | // 553 | // $custom-select-font-size-sm: 75% !default; 554 | // $custom-select-height-sm: $input-height-sm !default; 555 | // 556 | // $custom-select-font-size-lg: 125% !default; 557 | // $custom-select-height-lg: $input-height-lg !default; 558 | // 559 | // $custom-range-track-width: 100% !default; 560 | // $custom-range-track-height: .5rem !default; 561 | // $custom-range-track-cursor: pointer !default; 562 | // $custom-range-track-bg: $gray-300 !default; 563 | // $custom-range-track-border-radius: 1rem !default; 564 | // $custom-range-track-box-shadow: inset 0 .25rem .25rem rgba($black, .1) !default; 565 | // 566 | // $custom-range-thumb-width: 1rem !default; 567 | // $custom-range-thumb-height: $custom-range-thumb-width !default; 568 | // $custom-range-thumb-bg: $component-active-bg !default; 569 | // $custom-range-thumb-border: 0 !default; 570 | // $custom-range-thumb-border-radius: 1rem !default; 571 | // $custom-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default; 572 | // $custom-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-btn-focus-box-shadow !default; 573 | // $custom-range-thumb-focus-box-shadow-width: $input-btn-focus-width !default; // For focus box shadow issue in IE/Edge 574 | // $custom-range-thumb-active-bg: lighten($component-active-bg, 35%) !default; 575 | // 576 | // $custom-file-height: $input-height !default; 577 | // $custom-file-height-inner: $input-height-inner !default; 578 | // $custom-file-focus-border-color: $input-focus-border-color !default; 579 | // $custom-file-focus-box-shadow: $input-btn-focus-box-shadow !default; 580 | // $custom-file-disabled-bg: $input-disabled-bg !default; 581 | // 582 | // $custom-file-padding-y: $input-btn-padding-y !default; 583 | // $custom-file-padding-x: $input-btn-padding-x !default; 584 | // $custom-file-line-height: $input-btn-line-height !default; 585 | // $custom-file-color: $input-color !default; 586 | // $custom-file-bg: $input-bg !default; 587 | // $custom-file-border-width: $input-btn-border-width !default; 588 | // $custom-file-border-color: $input-border-color !default; 589 | // $custom-file-border-radius: $input-border-radius !default; 590 | // $custom-file-box-shadow: $input-box-shadow !default; 591 | // $custom-file-button-color: $custom-file-color !default; 592 | // $custom-file-button-bg: $input-group-addon-bg !default; 593 | // $custom-file-text: ( 594 | // en: "Browse" 595 | // ) !default; 596 | // 597 | // 598 | // // Form validation 599 | // $form-feedback-margin-top: $form-text-margin-top !default; 600 | // $form-feedback-font-size: $small-font-size !default; 601 | // $form-feedback-valid-color: theme-color("success") !default; 602 | // $form-feedback-invalid-color: theme-color("danger") !default; 603 | // 604 | // 605 | // // Dropdowns 606 | // // 607 | // // Dropdown menu container and contents. 608 | // 609 | // $dropdown-min-width: 10rem !default; 610 | // $dropdown-padding-y: .5rem !default; 611 | // $dropdown-spacer: .125rem !default; 612 | // $dropdown-bg: $white !default; 613 | // $dropdown-border-color: rgba($black, .15) !default; 614 | // $dropdown-border-radius: $border-radius !default; 615 | // $dropdown-border-width: $border-width !default; 616 | // $dropdown-divider-bg: $gray-200 !default; 617 | // $dropdown-box-shadow: 0 .5rem 1rem rgba($black, .175) !default; 618 | // 619 | // $dropdown-link-color: $gray-900 !default; 620 | // $dropdown-link-hover-color: darken($gray-900, 5%) !default; 621 | // $dropdown-link-hover-bg: $gray-100 !default; 622 | // 623 | // $dropdown-link-active-color: $component-active-color !default; 624 | // $dropdown-link-active-bg: $component-active-bg !default; 625 | // 626 | // $dropdown-link-disabled-color: $gray-600 !default; 627 | // 628 | $dropdown-item-padding-y: .45rem !default; 629 | // $dropdown-item-padding-x: 1.5rem !default; 630 | // 631 | // $dropdown-header-color: $gray-600 !default; 632 | // 633 | // 634 | // // Z-index master list 635 | // // 636 | // // Warning: Avoid customizing these values. They're used for a bird's eye view 637 | // // of components dependent on the z-axis and are designed to all work together. 638 | // 639 | // $zindex-dropdown: 1000 !default; 640 | // $zindex-sticky: 1020 !default; 641 | // $zindex-fixed: 1030 !default; 642 | // $zindex-modal-backdrop: 1040 !default; 643 | // $zindex-modal: 1050 !default; 644 | // $zindex-popover: 1060 !default; 645 | // $zindex-tooltip: 1070 !default; 646 | // 647 | // // Navs 648 | // 649 | $nav-link-padding-y: .8rem !default; 650 | // $nav-link-padding-x: 1rem !default; 651 | // $nav-link-disabled-color: $gray-600 !default; 652 | // 653 | // $nav-tabs-border-color: $gray-300 !default; 654 | // $nav-tabs-border-width: $border-width !default; 655 | // $nav-tabs-border-radius: $border-radius !default; 656 | // $nav-tabs-link-hover-border-color: $gray-200 $gray-200 $nav-tabs-border-color !default; 657 | // $nav-tabs-link-active-color: $gray-700 !default; 658 | // $nav-tabs-link-active-bg: $body-bg !default; 659 | // $nav-tabs-link-active-border-color: $gray-300 $gray-300 $nav-tabs-link-active-bg !default; 660 | // 661 | // $nav-pills-border-radius: $border-radius !default; 662 | // $nav-pills-link-active-color: $component-active-color !default; 663 | // $nav-pills-link-active-bg: $component-active-bg !default; 664 | // 665 | // $nav-divider-color: $gray-200 !default; 666 | // $nav-divider-margin-y: ($spacer / 2) !default; 667 | // 668 | // // Navbar 669 | // 670 | // $navbar-padding-y: ($spacer / 2) !default; 671 | // $navbar-padding-x: $spacer !default; 672 | // 673 | $navbar-nav-link-padding-x: .9rem !default; 674 | // 675 | // $navbar-brand-font-size: $font-size-lg !default; 676 | // // Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link 677 | // $nav-link-height: ($font-size-base * $line-height-base + $nav-link-padding-y * 2) !default; 678 | // $navbar-brand-height: $navbar-brand-font-size * $line-height-base !default; 679 | // $navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) / 2 !default; 680 | // 681 | // $navbar-toggler-padding-y: .25rem !default; 682 | // $navbar-toggler-padding-x: .75rem !default; 683 | // $navbar-toggler-font-size: $font-size-lg !default; 684 | // $navbar-toggler-border-radius: $btn-border-radius !default; 685 | // 686 | $navbar-dark-color: rgba($white, .85) !default; 687 | $navbar-dark-hover-color: rgba($white, 1) !default; 688 | // $navbar-dark-active-color: $white !default; 689 | // $navbar-dark-disabled-color: rgba($white, .25) !default; 690 | // $navbar-dark-toggler-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-dark-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"), "#", "%23") !default; 691 | // $navbar-dark-toggler-border-color: rgba($white, .1) !default; 692 | // 693 | // $navbar-light-color: rgba($black, .5) !default; 694 | // $navbar-light-hover-color: rgba($black, .7) !default; 695 | // $navbar-light-active-color: rgba($black, .9) !default; 696 | // $navbar-light-disabled-color: rgba($black, .3) !default; 697 | // $navbar-light-toggler-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-light-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"), "#", "%23") !default; 698 | // $navbar-light-toggler-border-color: rgba($black, .1) !default; 699 | // 700 | // // Pagination 701 | // 702 | $pagination-padding-y: .75rem !default; 703 | $pagination-padding-x: 1rem !default; 704 | // $pagination-padding-y-sm: .25rem !default; 705 | // $pagination-padding-x-sm: .5rem !default; 706 | // $pagination-padding-y-lg: .75rem !default; 707 | // $pagination-padding-x-lg: 1.5rem !default; 708 | // $pagination-line-height: 1.25 !default; 709 | // 710 | // $pagination-color: $link-color !default; 711 | // $pagination-bg: $white !default; 712 | // $pagination-border-width: $border-width !default; 713 | // $pagination-border-color: $gray-300 !default; 714 | // 715 | // $pagination-focus-box-shadow: $input-btn-focus-box-shadow !default; 716 | // $pagination-focus-outline: 0 !default; 717 | // 718 | // $pagination-hover-color: $link-hover-color !default; 719 | // $pagination-hover-bg: $gray-200 !default; 720 | // $pagination-hover-border-color: $gray-300 !default; 721 | // 722 | // $pagination-active-color: $component-active-color !default; 723 | // $pagination-active-bg: $component-active-bg !default; 724 | // $pagination-active-border-color: $pagination-active-bg !default; 725 | // 726 | // $pagination-disabled-color: $gray-600 !default; 727 | // $pagination-disabled-bg: $white !default; 728 | // $pagination-disabled-border-color: $gray-300 !default; 729 | // 730 | // 731 | // // Jumbotron 732 | // 733 | // $jumbotron-padding: 2rem !default; 734 | // $jumbotron-bg: $gray-200 !default; 735 | // 736 | // 737 | // // Cards 738 | // 739 | // $card-spacer-y: .75rem !default; 740 | // $card-spacer-x: 1.25rem !default; 741 | // $card-border-width: $border-width !default; 742 | // $card-border-radius: $border-radius !default; 743 | $card-border-color: $lightblue !default; 744 | // $card-inner-border-radius: calc(#{$card-border-radius} - #{$card-border-width}) !default; 745 | // $card-cap-bg: rgba($black, .03) !default; 746 | // $card-bg: $white !default; 747 | // 748 | // $card-img-overlay-padding: 1.25rem !default; 749 | // 750 | // $card-group-margin: ($grid-gutter-width / 2) !default; 751 | // $card-deck-margin: $card-group-margin !default; 752 | // 753 | // $card-columns-count: 3 !default; 754 | // $card-columns-gap: 1.25rem !default; 755 | // $card-columns-margin: $card-spacer-y !default; 756 | // 757 | // 758 | // // Tooltips 759 | // 760 | // $tooltip-font-size: $font-size-sm !default; 761 | // $tooltip-max-width: 200px !default; 762 | // $tooltip-color: $white !default; 763 | // $tooltip-bg: $black !default; 764 | // $tooltip-border-radius: $border-radius !default; 765 | // $tooltip-opacity: .9 !default; 766 | // $tooltip-padding-y: .25rem !default; 767 | // $tooltip-padding-x: .5rem !default; 768 | // $tooltip-margin: 0 !default; 769 | // 770 | // $tooltip-arrow-width: .8rem !default; 771 | // $tooltip-arrow-height: .4rem !default; 772 | // $tooltip-arrow-color: $tooltip-bg !default; 773 | // 774 | // 775 | // // Popovers 776 | // 777 | // $popover-font-size: $font-size-sm !default; 778 | // $popover-bg: $white !default; 779 | // $popover-max-width: 276px !default; 780 | // $popover-border-width: $border-width !default; 781 | // $popover-border-color: rgba($black, .2) !default; 782 | // $popover-border-radius: $border-radius-lg !default; 783 | // $popover-box-shadow: 0 .25rem .5rem rgba($black, .2) !default; 784 | // 785 | // $popover-header-bg: darken($popover-bg, 3%) !default; 786 | // $popover-header-color: $headings-color !default; 787 | // $popover-header-padding-y: .5rem !default; 788 | // $popover-header-padding-x: .75rem !default; 789 | // 790 | // $popover-body-color: $body-color !default; 791 | // $popover-body-padding-y: $popover-header-padding-y !default; 792 | // $popover-body-padding-x: $popover-header-padding-x !default; 793 | // 794 | // $popover-arrow-width: 1rem !default; 795 | // $popover-arrow-height: .5rem !default; 796 | // $popover-arrow-color: $popover-bg !default; 797 | // 798 | // $popover-arrow-outer-color: fade-in($popover-border-color, .05) !default; 799 | // 800 | // 801 | // // Badges 802 | // 803 | $badge-font-size: 84% !default; 804 | // $badge-font-weight: $font-weight-bold !default; 805 | // $badge-padding-y: .25em !default; 806 | // $badge-padding-x: .4em !default; 807 | // $badge-border-radius: $border-radius !default; 808 | // 809 | // $badge-pill-padding-x: .6em !default; 810 | // // Use a higher than normal value to ensure completely rounded edges when 811 | // // customizing padding or font-size on labels. 812 | // $badge-pill-border-radius: 10rem !default; 813 | // 814 | // 815 | // // Modals 816 | // 817 | // // Padding applied to the modal body 818 | // $modal-inner-padding: 1rem !default; 819 | // 820 | // $modal-dialog-margin: .5rem !default; 821 | // $modal-dialog-margin-y-sm-up: 1.75rem !default; 822 | // 823 | // $modal-title-line-height: $line-height-base !default; 824 | // 825 | // $modal-content-bg: $white !default; 826 | // $modal-content-border-color: rgba($black, .2) !default; 827 | // $modal-content-border-width: $border-width !default; 828 | // $modal-content-border-radius: $border-radius-lg !default; 829 | // $modal-content-box-shadow-xs: 0 .25rem .5rem rgba($black, .5) !default; 830 | // $modal-content-box-shadow-sm-up: 0 .5rem 1rem rgba($black, .5) !default; 831 | // 832 | // $modal-backdrop-bg: $black !default; 833 | // $modal-backdrop-opacity: .5 !default; 834 | // $modal-header-border-color: $gray-200 !default; 835 | // $modal-footer-border-color: $modal-header-border-color !default; 836 | // $modal-header-border-width: $modal-content-border-width !default; 837 | // $modal-footer-border-width: $modal-header-border-width !default; 838 | // $modal-header-padding: 1rem !default; 839 | // 840 | // $modal-lg: 800px !default; 841 | // $modal-md: 500px !default; 842 | // $modal-sm: 300px !default; 843 | // 844 | // $modal-transition: transform .3s ease-out !default; 845 | // 846 | // 847 | // // Alerts 848 | // // 849 | // // Define alert colors, border radius, and padding. 850 | // 851 | // $alert-padding-y: .75rem !default; 852 | // $alert-padding-x: 1.25rem !default; 853 | // $alert-margin-bottom: 1rem !default; 854 | // $alert-border-radius: $border-radius !default; 855 | // $alert-link-font-weight: $font-weight-bold !default; 856 | // $alert-border-width: $border-width !default; 857 | // 858 | $alert-bg-level: 0 !default; 859 | $alert-border-level: 0 !default; 860 | $alert-color-level: 0 !default; 861 | // 862 | // 863 | // // Progress bars 864 | // 865 | // $progress-height: 1rem !default; 866 | // $progress-font-size: ($font-size-base * .75) !default; 867 | // $progress-bg: $gray-200 !default; 868 | // $progress-border-radius: $border-radius !default; 869 | // $progress-box-shadow: inset 0 .1rem .1rem rgba($black, .1) !default; 870 | // $progress-bar-color: $white !default; 871 | // $progress-bar-bg: theme-color("primary") !default; 872 | // $progress-bar-animation-timing: 1s linear infinite !default; 873 | // $progress-bar-transition: width .6s ease !default; 874 | // 875 | // // List group 876 | // 877 | // $list-group-bg: $white !default; 878 | // $list-group-border-color: rgba($black, .125) !default; 879 | // $list-group-border-width: $border-width !default; 880 | // $list-group-border-radius: $border-radius !default; 881 | // 882 | // $list-group-item-padding-y: .75rem !default; 883 | // $list-group-item-padding-x: 1.25rem !default; 884 | // 885 | // $list-group-hover-bg: $gray-100 !default; 886 | // $list-group-active-color: $component-active-color !default; 887 | // $list-group-active-bg: $component-active-bg !default; 888 | // $list-group-active-border-color: $list-group-active-bg !default; 889 | // 890 | // $list-group-disabled-color: $gray-600 !default; 891 | // $list-group-disabled-bg: $list-group-bg !default; 892 | // 893 | // $list-group-action-color: $gray-700 !default; 894 | // $list-group-action-hover-color: $list-group-action-color !default; 895 | // 896 | // $list-group-action-active-color: $body-color !default; 897 | // $list-group-action-active-bg: $gray-200 !default; 898 | // 899 | // 900 | // // Image thumbnails 901 | // 902 | // $thumbnail-padding: .25rem !default; 903 | // $thumbnail-bg: $body-bg !default; 904 | // $thumbnail-border-width: $border-width !default; 905 | // $thumbnail-border-color: $gray-300 !default; 906 | // $thumbnail-border-radius: $border-radius !default; 907 | // $thumbnail-box-shadow: 0 1px 2px rgba($black, .075) !default; 908 | // 909 | // 910 | // // Figures 911 | // 912 | // $figure-caption-font-size: 90% !default; 913 | // $figure-caption-color: $gray-600 !default; 914 | // 915 | // 916 | // // Breadcrumbs 917 | // 918 | // $breadcrumb-padding-y: .75rem !default; 919 | // $breadcrumb-padding-x: 1rem !default; 920 | // $breadcrumb-item-padding: .5rem !default; 921 | // 922 | // $breadcrumb-margin-bottom: 1rem !default; 923 | // 924 | // $breadcrumb-bg: $gray-200 !default; 925 | // $breadcrumb-divider-color: $gray-600 !default; 926 | // $breadcrumb-active-color: $gray-600 !default; 927 | // $breadcrumb-divider: quote("/") !default; 928 | // 929 | // $breadcrumb-border-radius: $border-radius !default; 930 | // 931 | // 932 | // // Carousel 933 | // 934 | // $carousel-control-color: $white !default; 935 | // $carousel-control-width: 15% !default; 936 | // $carousel-control-opacity: .5 !default; 937 | // 938 | // $carousel-indicator-width: 30px !default; 939 | // $carousel-indicator-height: 3px !default; 940 | // $carousel-indicator-spacer: 3px !default; 941 | // $carousel-indicator-active-bg: $white !default; 942 | // 943 | // $carousel-caption-width: 70% !default; 944 | // $carousel-caption-color: $white !default; 945 | // 946 | // $carousel-control-icon-width: 20px !default; 947 | // 948 | // $carousel-control-prev-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E"), "#", "%23") !default; 949 | // $carousel-control-next-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E"), "#", "%23") !default; 950 | // 951 | // $carousel-transition: transform .6s ease !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`) 952 | // 953 | // 954 | // // Close 955 | // 956 | // $close-font-size: $font-size-base * 1.5 !default; 957 | // $close-font-weight: $font-weight-bold !default; 958 | // $close-color: $black !default; 959 | // $close-text-shadow: 0 1px 0 $white !default; 960 | // 961 | // // Code 962 | // 963 | // $code-font-size: 87.5% !default; 964 | // $code-color: $pink !default; 965 | // 966 | // $kbd-padding-y: .2rem !default; 967 | // $kbd-padding-x: .4rem !default; 968 | // $kbd-font-size: $code-font-size !default; 969 | // $kbd-color: $white !default; 970 | // $kbd-bg: $gray-900 !default; 971 | // 972 | // $pre-color: $gray-900 !default; 973 | // $pre-scrollable-max-height: 340px !default; 974 | // 975 | // 976 | // // Printing 977 | // $print-page-size: a3 !default; 978 | // $print-body-min-width: map-get($grid-breakpoints, "lg") !default; 979 | 980 | 981 | // --------------------------------------- !! 982 | // Additional theme variables 983 | // --------------------------------------- !! 984 | 985 | // Buttons 986 | 987 | $round-radius: 30px !default; --------------------------------------------------------------------------------
37 | 38 | 39 | {post.category.name} 40 | 41 | 42 |