├── .gitattributes
├── .gitignore
├── README-template.md
├── README.md
├── app
├── js
│ └── script.js
└── scss
│ ├── components
│ ├── _index.scss
│ ├── card-grid.scss
│ ├── card.scss
│ ├── header.scss
│ └── toggle.scss
│ ├── globals
│ ├── _index.scss
│ ├── boilerplate.scss
│ ├── colors.scss
│ ├── fonts.scss
│ ├── layout.scss
│ └── typography.scss
│ ├── style.scss
│ └── util
│ ├── _index.scss
│ ├── breakpoints.scss
│ └── functions.scss
├── gulpfile.js
├── images
├── favicon-32x32.png
├── icon-down.svg
├── icon-facebook.svg
├── icon-instagram.svg
├── icon-twitter.svg
├── icon-up.svg
└── icon-youtube.svg
├── index-accessibility.html
├── index.html
├── notes.md
├── package-lock.json
├── package.json
└── style-guide.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Avoid accidental Sketch file upload
2 | ###############################################
3 | ## Please do not remove line 5 - thanks! 🙂 ##
4 | ###############################################
5 | *.sketch
6 |
7 | # Avoid accidental XD or Figma upload if you convert the design file
8 | #######################################################
9 | ## Please do not remove lines 11 and 12 - thanks! 🙂 ##
10 | #######################################################
11 | *.xd
12 | *.fig
13 |
14 | # Avoid your project being littered with annoying .DS_Store files!
15 | .DS_Store
16 |
17 | node_modules/
18 | dist/
19 | design/
--------------------------------------------------------------------------------
/README-template.md:
--------------------------------------------------------------------------------
1 | # Frontend Mentor - Social media dashboard with theme switcher solution
2 |
3 | This is a solution to the [Social media dashboard with theme switcher challenge on Frontend Mentor](https://www.frontendmentor.io/challenges/social-media-dashboard-with-theme-switcher-6oY8ozp_H). Frontend Mentor challenges help you improve your coding skills by building realistic projects.
4 |
5 | ## Table of contents
6 |
7 | - [Overview](#overview)
8 | - [The challenge](#the-challenge)
9 | - [Screenshot](#screenshot)
10 | - [Links](#links)
11 | - [My process](#my-process)
12 | - [Built with](#built-with)
13 | - [What I learned](#what-i-learned)
14 | - [Continued development](#continued-development)
15 | - [Useful resources](#useful-resources)
16 | - [Author](#author)
17 | - [Acknowledgments](#acknowledgments)
18 |
19 | **Note: Delete this note and update the table of contents based on what sections you keep.**
20 |
21 | ## Overview
22 |
23 | ### The challenge
24 |
25 | Users should be able to:
26 |
27 | - View the optimal layout for the site depending on their device's screen size
28 | - See hover states for all interactive elements on the page
29 | - Toggle color theme to their preference
30 |
31 | ### Screenshot
32 |
33 | 
34 |
35 | Add a screenshot of your solution. The easiest way to do this is to use Firefox to view your project, right-click the page and select "Take a Screenshot". You can choose either a full-height screenshot or a cropped one based on how long the page is. If it's very long, it might be best to crop it.
36 |
37 | Alternatively, you can use a tool like [FireShot](https://getfireshot.com/) to take the screenshot. FireShot has a free option, so you don't need to purchase it.
38 |
39 | Then crop/optimize/edit your image however you like, add it to your project, and update the file path in the image above.
40 |
41 | **Note: Delete this note and the paragraphs above when you add your screenshot. If you prefer not to add a screenshot, feel free to remove this entire section.**
42 |
43 | ### Links
44 |
45 | - Solution URL: [Add solution URL here](https://your-solution-url.com)
46 | - Live Site URL: [Add live site URL here](https://your-live-site-url.com)
47 |
48 | ## My process
49 |
50 | ### Built with
51 |
52 | - Semantic HTML5 markup
53 | - CSS custom properties
54 | - Flexbox
55 | - CSS Grid
56 | - Mobile-first workflow
57 | - [React](https://reactjs.org/) - JS library
58 | - [Next.js](https://nextjs.org/) - React framework
59 | - [Styled Components](https://styled-components.com/) - For styles
60 |
61 | **Note: These are just examples. Delete this note and replace the list above with your own choices**
62 |
63 | ### What I learned
64 |
65 | Use this section to recap over some of your major learnings while working through this project. Writing these out and providing code samples of areas you want to highlight is a great way to reinforce your own knowledge.
66 |
67 | To see how you can add code snippets, see below:
68 |
69 | ```html
70 |
Some HTML code I'm proud of
71 | ```
72 | ```css
73 | .proud-of-this-css {
74 | color: papayawhip;
75 | }
76 | ```
77 | ```js
78 | const proudOfThisFunc = () => {
79 | console.log('🎉')
80 | }
81 | ```
82 |
83 | If you want more help with writing markdown, we'd recommend checking out [The Markdown Guide](https://www.markdownguide.org/) to learn more.
84 |
85 | **Note: Delete this note and the content within this section and replace with your own learnings.**
86 |
87 | ### Continued development
88 |
89 | Use this section to outline areas that you want to continue focusing on in future projects. These could be concepts you're still not completely comfortable with or techniques you found useful that you want to refine and perfect.
90 |
91 | **Note: Delete this note and the content within this section and replace with your own plans for continued development.**
92 |
93 | ### Useful resources
94 |
95 | - [Example resource 1](https://www.example.com) - This helped me for XYZ reason. I really liked this pattern and will use it going forward.
96 | - [Example resource 2](https://www.example.com) - This is an amazing article which helped me finally understand XYZ. I'd recommend it to anyone still learning this concept.
97 |
98 | **Note: Delete this note and replace the list above with resources that helped you during the challenge. These could come in handy for anyone viewing your solution or for yourself when you look back on this project in the future.**
99 |
100 | ## Author
101 |
102 | - Website - [Add your name here](https://www.your-site.com)
103 | - Frontend Mentor - [@yourusername](https://www.frontendmentor.io/profile/yourusername)
104 | - Twitter - [@yourusername](https://www.twitter.com/yourusername)
105 |
106 | **Note: Delete this note and add/remove/edit lines above based on what links you'd like to share.**
107 |
108 | ## Acknowledgments
109 |
110 | This is where you can give a hat tip to anyone who helped you out on this project. Perhaps you worked in a team or got some inspiration from someone else's solution. This is the perfect place to give them some credit.
111 |
112 | **Note: Delete this note and edit this section's content as necessary. If you completed this challenge by yourself, feel free to delete this section entirely.**
113 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Frontend Mentor - Social media dashboard with theme switcher
2 |
3 | This code is from my [YouTube video series](https://www.youtube.com/watch?v=iL4irerdGdU&list=PLUWqFDiirlsu5az5EIyxe8ZddyNO_kDuP) building a social media dashboard with dark/light toggle.
4 |
5 | [Frontend Mentor challenge](https://www.frontendmentor.io/challenges/social-media-dashboard-with-theme-switcher-6oY8ozp_H)
6 |
7 | Check out the live website [here](https://codercoder-darklight-toggle.pages.dev/)!
8 |
--------------------------------------------------------------------------------
/app/js/script.js:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | The first time the page is loaded, the color mode set on the preference
4 | is used and set as 'default' in the local storage.
5 | Changing the default preferences works the same way as changing the
6 | color mode using the buttons, if the page is loaded.
7 | When the page is reloaded, whatever is the value set on the local storage
8 | has precedence over the values in the preference. If the preference
9 | changed after the page was visited - and the page is not loaded -
10 | the last value saved on the local storage is loaded.
11 | */
12 |
13 | const darkButton = document.getElementById('dark');
14 | const lightButton = document.getElementById('light');
15 |
16 | const setDarkMode = () => {
17 | document.querySelector('body').classList = 'dark';
18 | localStorage.setItem('colorMode', 'dark');
19 | };
20 |
21 | const setLightMode = () => {
22 | document.querySelector('body').classList = 'light';
23 | localStorage.setItem('colorMode', 'light');
24 | };
25 |
26 | const colorModeFromLocalStorage = () => {
27 | return localStorage.getItem('colorMode');
28 | };
29 |
30 | const colorModeFromPreferences = () => {
31 | return window.matchMedia('(prefers-color-scheme: dark)').matches
32 | ? 'dark'
33 | : 'light' // If preference is set or does not match anything (light is default)
34 | };
35 |
36 | const loadAndUpdateColor = () => {
37 | // local storage has precendence over the prefers-color-scheme
38 | const color = colorModeFromLocalStorage() || colorModeFromPreferences();
39 | color == 'dark' ? darkButton.click() : lightButton.click();
40 | };
41 |
42 | // when the inputs are clicked, check which radio button is checked and change the color
43 | const radioButtons = document.querySelectorAll('.toggle__wrapper input');
44 | radioButtons.forEach(button => {
45 | button.addEventListener('click', (event) => {
46 | darkButton.checked ? setDarkMode() : setLightMode();
47 | });
48 | });
49 |
50 | // when the prefers-color-scheme changes, this event will be emitted
51 | // event reflects the media query, if it matches, the new color is dark, else it is light
52 | window.matchMedia('(prefers-color-scheme: dark)')
53 | .addEventListener('change', (event) => {
54 | event.matches ? darkButton.click() : lightButton.click();
55 | });
56 |
57 | // Load the right color on startup - localStorage has precedence
58 | loadAndUpdateColor();
--------------------------------------------------------------------------------
/app/scss/components/_index.scss:
--------------------------------------------------------------------------------
1 | @forward 'header';
2 | @forward 'toggle';
3 | @forward 'card';
4 | @forward 'card-grid';
5 |
--------------------------------------------------------------------------------
/app/scss/components/card-grid.scss:
--------------------------------------------------------------------------------
1 | @use '../util' as *;
2 |
3 | .card-grid {
4 | display: grid;
5 | grid-template-columns: repeat(2, 1fr);
6 | grid-template-rows: repeat(2, auto);
7 | justify-items: start;
8 | gap: rem(23);
9 |
10 | .card__subtitle {
11 | }
12 |
13 | .card__count {
14 | margin-bottom: 0;
15 | }
16 |
17 | .card__count,
18 | .card__change {
19 | align-self: end;
20 | }
21 |
22 | img,
23 | .card__change {
24 | justify-self: end;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/scss/components/card.scss:
--------------------------------------------------------------------------------
1 | @use '../util' as *;
2 |
3 | .cards {
4 | display: grid;
5 | grid-template-columns: 1fr;
6 | gap: rem(30);
7 |
8 | @include breakpoint(medium) {
9 | grid-template-columns: repeat(2, 1fr);
10 | }
11 |
12 | @include breakpoint(large) {
13 | grid-template-columns: repeat(4, 1fr);
14 | }
15 | }
16 |
17 | .card {
18 | position: relative;
19 | overflow: hidden;
20 | background: var(--card-bg);
21 | color: var(--dark-text1);
22 | padding: rem(25);
23 | border-radius: rem(5);
24 | text-align: center;
25 | transition: background 150ms ease-in-out;
26 | cursor: pointer;
27 |
28 | &:hover {
29 | background: var(--card-hover);
30 | }
31 |
32 | &--facebook {
33 | border-top: rem(5) solid var(--facebook);
34 | }
35 |
36 | &--twitter {
37 | border-top: rem(5) solid var(--twitter);
38 | }
39 |
40 | &--instagram {
41 | padding-top: rem(30);
42 |
43 | &::before {
44 | content: '';
45 | position: absolute;
46 | display: block;
47 | left: 0;
48 | top: 0;
49 | width: 100%;
50 | height: rem(5);
51 | background: linear-gradient(
52 | 225deg,
53 | var(--instagram-end),
54 | var(--instagram-middle) 50.91%,
55 | var(--instagram-start) 100%
56 | ); // var(--instagram-start);
57 | }
58 | //border-top: rem(5) solid linear-gradient(225deg, hsl(329, 70%, 58%) 0%, hsl(5, 77%, 71%) 50.91%, #fdc366 100%);
59 | }
60 |
61 | &--youtube {
62 | border-top: rem(5) solid var(--youtube);
63 | }
64 |
65 | &__platform {
66 | display: flex;
67 | justify-content: center;
68 | align-items: center;
69 | height: rem(20);
70 | margin-top: rem(5);
71 | margin-bottom: rem(28);
72 | }
73 |
74 | &__subtitle {
75 | font-size: rem(14);
76 | font-weight: 700;
77 | color: var(--text-color2);
78 | }
79 |
80 | &__icon {
81 | margin-right: rem(8);
82 |
83 | &--facebook {
84 | }
85 |
86 | &--twitter {
87 | }
88 |
89 | &--instagram {
90 | }
91 |
92 | &--youtube {
93 | }
94 | }
95 |
96 | &__username {
97 | font-size: rem(12);
98 | font-weight: 700;
99 | color: var(--text-color2);
100 | }
101 |
102 | &__followers {
103 | margin-bottom: rem(25);
104 | }
105 |
106 | &__count {
107 | color: var(--text-color);
108 | font-weight: 700;
109 | letter-spacing: rem(-2);
110 | line-height: 1;
111 | margin-bottom: rem(4);
112 |
113 | &--big {
114 | font-size: rem(56);
115 | }
116 |
117 | &--small {
118 | font-size: rem(32);
119 | }
120 | }
121 |
122 | &__label {
123 | font-size: rem(12);
124 | letter-spacing: rem(5);
125 | font-weight: 400;
126 | color: var(--text-color2);
127 | text-transform: uppercase;
128 | }
129 |
130 | &__change {
131 | display: flex;
132 | align-items: center;
133 | justify-content: center;
134 | font-size: rem(12);
135 | font-weight: 700;
136 |
137 | &--up {
138 | color: var(--limegreen);
139 | }
140 |
141 | &--down {
142 | color: var(--brightred);
143 | }
144 |
145 | img {
146 | margin-right: rem(4);
147 | }
148 | }
149 |
150 | &__number {
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/app/scss/components/header.scss:
--------------------------------------------------------------------------------
1 | @use '../util' as *;
2 |
3 | .header {
4 | display: flex;
5 | flex-wrap: wrap;
6 | margin-top: rem(36);
7 |
8 | @include breakpoint(medium) {
9 | justify-content: space-between;
10 | align-items: center;
11 | }
12 |
13 | &__title {
14 | width: 100%;
15 |
16 | @include breakpoint(medium) {
17 | width: auto;
18 | }
19 | }
20 |
21 | &__subtitle {
22 | font-size: rem(14);
23 | font-weight: 700;
24 | color: var(--text-color2);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/scss/components/toggle.scss:
--------------------------------------------------------------------------------
1 | @use '../util' as *;
2 | // https://codepen.io/SaraSoueidan/pen/jpBbrq/?editors=1100
3 |
4 | .toggle {
5 | display: grid;
6 | grid-template-columns: 1fr 3rem;
7 | border: none;
8 | margin: 0;
9 |
10 | label {
11 | font-size: rem(14);
12 | font-weight: 700;
13 | color: var(--toggle);
14 |
15 | &[for='dark'] {
16 | line-height: rem(24);
17 | margin-right: rem(13);
18 | }
19 | }
20 |
21 | &__wrapper {
22 | position: relative;
23 | height: rem(24);
24 | }
25 |
26 | input[type='radio'] {
27 | margin: 0 rem(-2) 0 rem(-2);
28 | opacity: 0;
29 | width: rem(24);
30 | height: rem(24);
31 |
32 | &:focus ~ .toggle__button {
33 | border: 2px solid white;
34 | }
35 | }
36 |
37 | &__background {
38 | display: block;
39 | height: 100%;
40 | position: absolute;
41 | width: 100%;
42 | top: 0;
43 | border-radius: rem(12);
44 | background: var(--toggle-bg);
45 | pointer-events: none;
46 | }
47 |
48 | &__button {
49 | position: absolute;
50 | left: rem(3);
51 | top: rem(3);
52 | right: 100%;
53 | width: rem(18);
54 | height: rem(18);
55 | border-radius: 50%;
56 | background-color: var(--toggle-button);
57 | transition: all 150ms ease-in-out;
58 | }
59 |
60 | #light:checked ~ .toggle__button {
61 | left: calc(100% - 21px);
62 | right: 3px;
63 | }
64 |
65 | #system:checked ~ .toggle__button {
66 | left: 50%;
67 | right: auto;
68 | transform: translate(-50%);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/scss/globals/_index.scss:
--------------------------------------------------------------------------------
1 | @forward 'fonts';
2 | @forward 'colors';
3 | @forward 'boilerplate';
4 | @forward 'typography';
5 | @forward 'layout';
6 |
--------------------------------------------------------------------------------
/app/scss/globals/boilerplate.scss:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 100%;
3 | box-sizing: border-box;
4 | }
5 |
6 | *,
7 | *::before,
8 | *::after {
9 | box-sizing: inherit;
10 | }
11 |
12 | body {
13 | margin: 0;
14 | padding: 0;
15 | font-family: var(--font-inter);
16 | background: var(--background);
17 | color: var(--text-color);
18 | }
19 |
20 | .visually-hidden {
21 | position: absolute;
22 | left: -10000px;
23 | top: auto;
24 | width: 1px;
25 | height: 1px;
26 | overflow: hidden;
27 | }
28 |
--------------------------------------------------------------------------------
/app/scss/globals/colors.scss:
--------------------------------------------------------------------------------
1 | :root {
2 | --limegreen: hsl(163, 72%, 41%);
3 | --brightred: hsl(356, 69%, 56%);
4 | --facebook: hsl(208, 92%, 53%);
5 | --twitter: hsl(203, 89%, 53%);
6 | --instagram-start: hsl(37, 97%, 70%);
7 | --instagram-middle: hsl(5, 77%, 71%);
8 | --instagram-end: hsl(329, 70%, 58%);
9 | --youtube: hsl(348, 97%, 39%);
10 | --toggle-bg-light: hsl(230, 22%, 74%);
11 | --toggle-bg-start: hsl(210, 78%, 56%);
12 | --toggle-bg-end: hsl(146, 68%, 55%);
13 | --toggle-light: hsl(230, 19%, 60%);
14 | --toggle-button-light: hsl(228, 46%, 96%);
15 | --dark-bg: hsl(230, 17%, 14%);
16 | --dark-top-bg: hsl(232, 19%, 15%);
17 | --dark-card: hsl(228, 28%, 20%);
18 | --dark-card-hover: hsl(228, 25%, 27%);
19 | --dark-text1: hsl(228, 34%, 66%);
20 | --dark-text2: hsl(0, 0%, 100%);
21 | --light-bg: hsl(0, 0%, 100%);
22 | --light-top-bg: hsl(225, 100%, 98%);
23 | --light-card: hsl(227, 47%, 96%);
24 | --light-card-hover: hsl(228, 33%, 91%);
25 | --light-text1: hsl(230, 12%, 44%);
26 | --light-text2: hsl(230, 17%, 14%);
27 | --background: var(--light-bg);
28 | --text-color: var(--light-text2);
29 | --text-color2: var(--light-text1);
30 | --card-bg: var(--light-card);
31 | --card-hover: var(--light-card-hover);
32 | --toggle: var(--toggle-light);
33 | --toggle-bg: var(--toggle-bg-light);
34 | --toggle-button: var(--toggle-button-light);
35 | }
36 |
37 | @media (prefers-color-scheme: dark) {
38 | :root {
39 | --background: var(--dark-bg);
40 | --text-color: var(--dark-text2);
41 | --text-color2: var(--dark-text1);
42 | --card-bg: var(--dark-card);
43 | --card-hover: var(--dark-card-hover);
44 | --toggle: var(--light-bg);
45 | --toggle-bg: linear-gradient(
46 | 225deg,
47 | var(--toggle-bg-end) 0%,
48 | var(--toggle-bg-start) 98.02%
49 | );
50 | --toggle-button: var(--dark-bg);
51 | }
52 | }
53 |
54 | body.light {
55 | --background: var(--light-bg);
56 | --text-color: var(--light-text2);
57 | --text-color2: var(--light-text1);
58 | --card-bg: var(--light-card);
59 | --card-hover: var(--light-card-hover);
60 | --toggle: var(--toggle-light);
61 | --toggle-bg: var(--toggle-bg-light);
62 | --toggle-button: var(--toggle-button-light);
63 | }
64 |
65 | body.dark {
66 | --background: var(--dark-bg);
67 | --text-color: var(--dark-text2);
68 | --text-color2: var(--dark-text1);
69 | --card-bg: var(--dark-card);
70 | --card-hover: var(--dark-card-hover);
71 | --toggle: var(--light-bg);
72 | --toggle-bg: linear-gradient(
73 | 225deg,
74 | var(--toggle-bg-end) 0%,
75 | var(--toggle-bg-start) 98.02%
76 | );
77 | --toggle-button: var(--dark-bg);
78 | }
79 |
--------------------------------------------------------------------------------
/app/scss/globals/fonts.scss:
--------------------------------------------------------------------------------
1 | :root {
2 | --font-inter: 'Inter', sans-serif;
3 | }
4 |
--------------------------------------------------------------------------------
/app/scss/globals/layout.scss:
--------------------------------------------------------------------------------
1 | @use '../util' as *;
2 |
3 | .container {
4 | padding: 0 rem(25);
5 | max-width: rem(1110);
6 | margin: 0 auto rem(46);
7 |
8 | @include breakpoint(large) {
9 | padding: 0;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/app/scss/globals/typography.scss:
--------------------------------------------------------------------------------
1 | @use '../util' as *;
2 |
3 | h1,
4 | h2,
5 | h3 {
6 | margin-top: 0;
7 | line-height: 1.1;
8 | }
9 |
10 | h1 {
11 | font-size: rem(24);
12 | margin-bottom: rem(3);
13 | @include breakpoint(large) {
14 | font-size: rem(28);
15 | }
16 | }
17 |
18 | h2 {
19 | font-size: rem(24);
20 | margin-bottom: rem(24);
21 | color: var(--text-color2);
22 | }
23 |
24 | a,
25 | a:visited,
26 | a:active {
27 | text-decoration: none;
28 | }
29 |
--------------------------------------------------------------------------------
/app/scss/style.scss:
--------------------------------------------------------------------------------
1 | @use 'globals';
2 | @use 'components';
3 |
--------------------------------------------------------------------------------
/app/scss/util/_index.scss:
--------------------------------------------------------------------------------
1 | @forward 'breakpoints';
2 | @forward 'functions';
3 |
--------------------------------------------------------------------------------
/app/scss/util/breakpoints.scss:
--------------------------------------------------------------------------------
1 | // 640px, 1150px, 1400px
2 | $breakpoints-up: (
3 | 'medium': '40em',
4 | 'large': '71.875em',
5 | 'xlarge': '87.5em',
6 | );
7 |
8 | // 639px, 1149px, 1399px
9 | $breakpoints-down: (
10 | 'small': '39.9375em',
11 | 'medium': '71.8125em',
12 | 'large': '87.4375em',
13 | );
14 |
15 | @mixin breakpoint($size) {
16 | @media (min-width: map-get($breakpoints-up, $size)) {
17 | @content;
18 | }
19 | }
20 |
21 | @mixin breakpoint-down($size) {
22 | @media (max-width: map-get($breakpoints-down, $size)) {
23 | @content;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/scss/util/functions.scss:
--------------------------------------------------------------------------------
1 | @use "sass:math";
2 |
3 | // Source: https://css-tricks.com/snippets/sass/px-to-em-functions/
4 | @function rem($pixels, $context: 16) {
5 | @return (math.div($pixels, $context)) * 1rem;
6 | }
7 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | // Initialize modules
2 | const { src, dest, watch, series } = require('gulp');
3 | const sass = require('gulp-sass')(require('sass'));
4 | const postcss = require('gulp-postcss');
5 | const autoprefixer = require('autoprefixer');
6 | const cssnano = require('cssnano');
7 | const babel = require('gulp-babel');
8 | const terser = require('gulp-terser');
9 | const browsersync = require('browser-sync').create();
10 |
11 | // Use dart-sass for @use
12 | //sass.compiler = require('dart-sass');
13 |
14 | // Sass Task
15 | function scssTask() {
16 | return src('app/scss/style.scss', { sourcemaps: true })
17 | .pipe(sass())
18 | .pipe(postcss([autoprefixer(), cssnano()]))
19 | .pipe(dest('dist', { sourcemaps: '.' }));
20 | }
21 |
22 | // JavaScript Task
23 | function jsTask() {
24 | return src('app/js/script.js', { sourcemaps: true })
25 | .pipe(babel({ presets: ['@babel/preset-env'] }))
26 | .pipe(terser())
27 | .pipe(dest('dist', { sourcemaps: '.' }));
28 | }
29 |
30 | // Browsersync
31 | function browserSyncServe(cb) {
32 | browsersync.init({
33 | server: {
34 | baseDir: '.',
35 | },
36 | notify: {
37 | styles: {
38 | top: 'auto',
39 | bottom: '0',
40 | },
41 | },
42 | });
43 | cb();
44 | }
45 | function browserSyncReload(cb) {
46 | browsersync.reload();
47 | cb();
48 | }
49 |
50 | // Watch Task
51 | function watchTask() {
52 | watch('*.html', browserSyncReload);
53 | watch(
54 | ['app/scss/**/*.scss', 'app/**/*.js'],
55 | series(scssTask, jsTask, browserSyncReload)
56 | );
57 | }
58 |
59 | // Default Gulp Task
60 | exports.default = series(scssTask, jsTask, browserSyncServe, watchTask);
61 |
62 | // Build Gulp Task
63 | exports.build = series(scssTask, jsTask);
64 |
--------------------------------------------------------------------------------
/images/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thecodercoder/fem-dklt-toggle/b918012b3b329f7b7135aba4ab83210ba6a8633b/images/favicon-32x32.png
--------------------------------------------------------------------------------
/images/icon-down.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/icon-facebook.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/icon-instagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/icon-twitter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/icon-up.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/icon-youtube.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/index-accessibility.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 |
15 |
16 | Frontend Mentor | Social Media Dashboard with Dark/Light Toggle
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 |
31 |
40 |
41 |
42 |
65 |
66 |
67 |
68 |
69 |
Facebook followers
70 |
78 |
79 |
1987
80 |
Followers
81 |
82 |
83 |
84 |
12 Today
85 |
86 |
87 |
88 |
106 |
107 |
108 |
116 |
117 |
11k
118 |
Followers
119 |
120 |
121 |
122 |
1099 Today
123 |
124 |
125 |
126 |
127 |
135 |
136 |
8239
137 |
Followers
138 |
139 |
140 |
141 |
144 Today
142 |
143 |
144 |
145 |
146 |
147 | Overview - Today
148 |
149 |
150 |
151 | Page Views for Facebook
152 |
153 |
154 |
87
155 |
156 |
157 |
3%
158 |
159 |
160 |
161 |
Likes
162 |
163 |
52
164 |
165 |
166 |
2%
167 |
168 |
169 |
170 |
Likes
171 |
172 |
5462
173 |
174 |
175 |
2257%
176 |
177 |
178 |
179 |
Profile Views
180 |
181 |
52k
182 |
183 |
184 |
1375%
185 |
186 |
187 |
188 |
Retweets
189 |
190 |
117
191 |
192 |
193 |
303%
194 |
195 |
196 |
197 |
Likes
198 |
199 |
507
200 |
201 |
202 |
553%
203 |
204 |
205 |
206 |
Likes
207 |
208 |
107
209 |
210 |
211 |
19%
212 |
213 |
214 |
215 |
Total Views
216 |
217 |
1407
218 |
219 |
220 |
12%
221 |
222 |
223 |
224 |
225 |
226 |
227 |
233 |
234 |
235 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 |
15 | Frontend Mentor | [Challenge Name Here]
16 |
17 |
18 |
19 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
38 |
39 |
40 |
63 |
64 |
65 |
66 |
67 |
75 |
76 |
1987
77 |
Followers
78 |
79 |
80 |
81 |
12 Today
82 |
83 |
84 |
85 |
103 |
104 |
105 |
113 |
114 |
11k
115 |
Followers
116 |
117 |
118 |
119 |
1099 Today
120 |
121 |
122 |
123 |
124 |
132 |
133 |
8239
134 |
Followers
135 |
136 |
137 |
138 |
144 Today
139 |
140 |
141 |
142 |
143 |
144 | Overview - Today
145 |
146 |
147 |
Page Views
148 |
149 |
87
150 |
151 |
152 |
3%
153 |
154 |
155 |
156 |
Likes
157 |
158 |
52
159 |
160 |
161 |
2%
162 |
163 |
164 |
165 |
Likes
166 |
167 |
5462
168 |
169 |
170 |
2257%
171 |
172 |
173 |
174 |
Profile Views
175 |
176 |
52k
177 |
178 |
179 |
1375%
180 |
181 |
182 |
183 |
Retweets
184 |
185 |
117
186 |
187 |
188 |
303%
189 |
190 |
191 |
192 |
Likes
193 |
194 |
507
195 |
196 |
197 |
553%
198 |
199 |
200 |
201 |
Likes
202 |
203 |
107
204 |
205 |
206 |
19%
207 |
208 |
209 |
210 |
Total Views
211 |
212 |
1407
213 |
214 |
215 |
12%
216 |
217 |
218 |
219 |
220 |
221 |
222 |
228 |
229 |
230 |
--------------------------------------------------------------------------------
/notes.md:
--------------------------------------------------------------------------------
1 | # Functional Requirements and Notes
2 |
3 | Light/Dark Mode toggle -- takes system pref by default, but can override with toggle
4 |
5 | What HTML markup (accessible) -- https://scottaohara.github.io/a11y_styled_form_controls/src/radio-button--switch/
6 |
7 | Use fieldset, legend, radio inputs
8 |
9 | Switching between light/dark modes via JS and Prefers-color-scheme media query -- https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
10 |
11 | https://piccalil.li/tutorial/create-a-user-controlled-dark-or-light-mode/
12 |
13 | Three option toggle: light/dark/system pref -- https://codepen.io/renddrew/pen/bRomab?editors=1100
14 |
15 | CSS Variables (custom properties) -- https://css-tricks.com/updating-a-css-variable-with-javascript/
16 |
17 | Accessibility
18 |
19 | - Use correct heading tags
20 | - Screenreader-only text for card titles/username -- https://www.accessibility-developer-guide.com/examples/hiding-elements/visually/
21 |
22 | NVDA hotkeys:
23 | https://webaim.org/resources/shortcuts/nvda
24 |
25 | - Ctrl will stop speech immediately
26 | - Caps Lock is NVDA key
27 | - NVDA + Q will quit NVDA
28 | - NVDA + B will read entire page from top to bottom
29 | - Press H to navigate through headline tags (other elements have hotkeys)
30 | - D will navigate through region/landmark
31 | - Landmarks/region are recognized by or tags, role="rolename" or aria-label
32 | - When NVDA recognizes a landmark/region, if no role or aria-label, it will read the first content
33 | - Press NVDA + down arrow to narrate rest of page from current position
34 | - Pressing Shift + H will navigate backwards
35 | - Go to Top?
36 | - Skip to content?
37 |
38 | Outline for video:
39 |
40 | - Accessibility overview
41 | - Install NVDA
42 | - basic controls, Ctrl, navigating headlines and regions and down
43 | - SaraSoueidan.com website
44 | - Try to read FEM site
45 | - Add:
46 | - title, fill in "yout name here" in footer
47 | - role="contentinfo" to footer
48 | - aria-label="" for platform overview and platform details
49 | - Add screenreader-only h3 tags for card titles
50 |
51 | --
52 |
53 | Semantic HTML and accessibility -- https://www.youtube.com/watch?v=qSNUi7pRmWg
54 | Inclusive cards -- https://inclusive-components.design/cards/
55 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fem-dklt-toggle",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "@babel/core": "^7.14.6",
14 | "@babel/preset-env": "^7.14.7",
15 | "autoprefixer": "^10.2.6",
16 | "browser-sync": "^2.27.4",
17 | "cssnano": "^5.0.6",
18 | "gulp": "^4.0.2",
19 | "gulp-babel": "^8.0.0",
20 | "gulp-postcss": "^9.0.0",
21 | "gulp-sass": "^5.0.0",
22 | "gulp-terser": "^2.0.1",
23 | "postcss": "^8.3.5",
24 | "sass": "^1.35.2"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/style-guide.md:
--------------------------------------------------------------------------------
1 | # Front-end Style Guide
2 |
3 | ## Layout
4 |
5 | The designs were created to the following widths:
6 |
7 | - Mobile: 375px
8 | - Desktop: 1440px
9 |
10 | ## Colors
11 |
12 | ### Primary
13 |
14 | - Lime Green: hsl(163, 72%, 41%)
15 | - Bright Red: hsl(356, 69%, 56%)
16 |
17 | - Facebook: hsl(208, 92%, 53%)
18 | - Twitter: hsl(203, 89%, 53%)
19 | - Instagram: linear gradient hsl(37, 97%, 70%) to hsl(329, 70%, 58%)
20 | - YouTube: hsl(348, 97%, 39%)
21 |
22 | #### Dark Theme
23 |
24 | - Toggle: linear gradient hsl(210, 78%, 56%) to hsl(146, 68%, 55%)
25 |
26 | #### Light Theme
27 |
28 | - Toggle: hsl(230, 22%, 74%)
29 |
30 | ### Neutral
31 |
32 | #### Dark Theme
33 |
34 | - Very Dark Blue (BG): hsl(230, 17%, 14%)
35 | - Very Dark Blue (Top BG Pattern): hsl(232, 19%, 15%)
36 | - Dark Desaturated Blue (Card BG): hsl(228, 28%, 20%)
37 | - Desaturated Blue (Text): hsl(228, 34%, 66%)
38 | - White (Text): hsl(0, 0%, 100%)
39 |
40 | #### Light Theme
41 |
42 | - White (BG): hsl(0, 0%, 100%)
43 | - Very Pale Blue (Top BG Pattern): hsl(225, 100%, 98%)
44 | - Light Grayish Blue (Card BG): hsl(227, 47%, 96%)
45 | - Dark Grayish Blue (Text): hsl(228, 12%, 44%)
46 | - Very Dark Blue (Text): hsl(230, 17%, 14%)
47 |
48 | ## Typography
49 |
50 | ### Body Copy
51 |
52 | - Font size (Overview Card Headings): 14px
53 |
54 | ### Font
55 |
56 | - Family: [Inter](https://fonts.google.com/specimen/Inter)
57 | - Weights: 400, 700
58 |
--------------------------------------------------------------------------------