Skip to content

Commit

Permalink
feat: dark mode (#182)
Browse files Browse the repository at this point in the history
* feat: add styles for dark mode

* feat: add further styles for dark mode

* docs: add mutation observer for dark mode class

* test: add visual regression tests for dark mode

* docs: fix button border in docs landing page

* docs: add dark mode feature to landing page

* docs: add logo for dark mode

* docs: add tip for dark mode
  • Loading branch information
tomosterlund committed Jun 23, 2023
1 parent 1dfdcd2 commit 7d42539
Show file tree
Hide file tree
Showing 31 changed files with 298 additions and 8 deletions.
44 changes: 44 additions & 0 deletions cypress/e2e/13-dark-mode.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import PageObject from "../support/page-object";

const {
setMonthMode,
setDayMode,
} = PageObject

describe('Dark mode on desktop', () => {
beforeEach(() => {
cy.visit('/#/cypress/dark-mode');
})

it('should render the week view in dark mode', () => {
cy.compareSnapshot('dark-mode-week-view');
});

it('should render the month view in dark mode', () => {
setMonthMode();
cy.compareSnapshot('dark-mode-month-view');
});

it('should render the day view in dark mode', () => {
setDayMode();
cy.compareSnapshot('dark-mode-day-view');
})
});


describe('Dark mode on mobile', () => {
beforeEach(() => {
cy.viewport('iphone-6');
cy.visit('/#/cypress/dark-mode');
})

it('should render the month view in dark mode', () => {
setMonthMode();
cy.compareSnapshot('dark-mode-month-view-mobile');
});

it('should render the day view in dark mode', () => {
setDayMode();
cy.compareSnapshot('dark-mode-day-view-mobile');
})
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions development/cypress/DarkMode.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<template>
<div class="page" style="color-scheme: dark">
<div class="wrapper">
<Qalendar
:config="config"
:selected-date="new Date(2022, (6 - 1), 16)"
:events="events"
/>
</div>
</div>
</template>

<script lang="ts">
import {defineComponent} from "vue";
import Qalendar from '../../src/Qalendar.vue';
import {fiveDayWeekEvents} from './__data__/02-five-day-week';
import {configInterface} from '../../src/typings/config.interface';
import {WEEK_START_DAY} from "../../src/helpers/Time";
export default defineComponent({
name: 'DarkMode',
components: {Qalendar},
data() {
return {
config: {
locale: 'de-DE',
week: {
startsOn: WEEK_START_DAY.MONDAY,
},
} as configInterface,
events: [],
}
},
mounted() {
this.events = fiveDayWeekEvents;
}
})
</script>

<style lang="scss" scoped>
.page {
width: 100%;
background-color: #000;
}
.wrapper {
margin-top: 24px;
max-width: 1400px;
height: 1000px;
margin-left: auto;
margin-right: auto;
}
</style>
2 changes: 2 additions & 0 deletions development/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import MultipleDayEvents from './cypress/MultipleDayEvents.vue'
import DayCell from './cypress/DayCell.vue'
import EventRenderingRegularDay from './cypress/EventRenderingRegularDay.vue'
import EventRenderingFlexibleDay from './cypress/EventRenderingFlexibleDay.vue'
import DarkMode from './cypress/DarkMode.vue'

const routes = [
{ path: "/", component: QalendarView },
Expand All @@ -37,6 +38,7 @@ const routes = [
{ path: "/cypress/events-regular-day", component: EventRenderingRegularDay },
{ path: "/cypress/events-flexible-day", component: EventRenderingFlexibleDay },
{ path: "/cypress/day-cell", component: DayCell },
{ path: "/cypress/dark-mode", component: DarkMode },
];

const router = createRouter({
Expand Down
7 changes: 5 additions & 2 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ export default {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2022-present Tom Österlund'
},
logo: 'https://discover-test-files.s3.eu-central-1.amazonaws.com/Q+MAIN.png',
logo: {
light: 'https://discover-test-files.s3.eu-central-1.amazonaws.com/Q+MAIN.png',
dark: 'https://discover-test-files.s3.eu-central-1.amazonaws.com/qalendar-dark-mode.svg'
},
},
appearance: false,
// appearance: false,
};
8 changes: 8 additions & 0 deletions docs/.vitepress/theme/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,11 @@
max-height: initial;
}
}

.dark .VPButton.medium.brand {
border-color: transparent;
}

.dark .VPHome .VPFeature img {
filter: invert(1);
}
22 changes: 22 additions & 0 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,26 @@
import DefaultTheme from 'vitepress/theme'
import './custom.css'

const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
const target = mutation.target as Element

if (mutation.attributeName === 'class') {
const newValue = target.getAttribute(mutation.attributeName)
if (newValue.includes('dark')) {
target.setAttribute('style', 'color-scheme: dark')
} else {
target.removeAttribute('style')
}
}
})
})

observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class'],
childList: false,
});


export default DefaultTheme
24 changes: 20 additions & 4 deletions docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,32 @@ export default {

## Style

As in the code example above, you need to import the styles for the component. Since Qalendar is
aiming to be a responsive multi-purpose component, it avoids use of fixed height and width where
As in the code example above, you need to import the styles for the component.

### Height & width

Since Qalendar is aiming to be a responsive multi-purpose component, it avoids use of fixed height
and width where
possible. Therefore, for most use-cases you would probably want to place it in a wrapper with a
fixed `height`, and possibly a `max-width`.

Qalendar takes a `config` prop, which contains all the most crucial options for configuring its
behavior. `config` is passed as an object, which could look like this:
### Dark Mode

Dark mode is enabled in one out of two ways:
1. User system preferences
2. Set programmatically, by adding the inline style `style="color-scheme: dark"` to a parent element.

::: tip
If the rest of your application does not have a dark mode, you might want to instruct
Qalendar never switch to its own dark mode, even when user system settings tell it so. You can
prevent this by wrapping it in an element with the inline style `style="color-scheme: light"`.
:::

## Basic configuration

Qalendar takes a `config` prop, which contains all the most crucial options for configuring its
behavior. `config` is passed as an object, which could look like this:

```js
data()
{
Expand Down
4 changes: 4 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ features:
src: /drag-drop.svg
title: Drag & Drop
details: Drag and drop events to change their date and time. Available on both desktop and mobile.
- icon:
src: /dark-mode.svg
title: Dark mode
details: Native support for dark mode
- icon:
src: /language.svg
title: Multi-language support
Expand Down
16 changes: 16 additions & 0 deletions docs/public/dark-mode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/Qalendar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ export default defineComponent({
<style lang="scss">
@import './styles/variables.scss';
@import './styles/mixins.scss';
@import '../node_modules/perfect-scrollbar/css/perfect-scrollbar.css';
.calendar-root-wrapper {
Expand All @@ -354,6 +355,12 @@ export default defineComponent({
display: flex;
flex-flow: column;
@include dark-mode {
background: #121212;
color: #fff;
border-color: transparent;
}
.top-bar-loader {
position: absolute;
top: 1px;
Expand Down Expand Up @@ -381,6 +388,10 @@ export default defineComponent({
);
animation: load 1.8s infinite;
border-radius: 16px;
@include dark-mode {
background: rgb(229, 224, 245);
}
}
@keyframes load {
Expand Down
18 changes: 17 additions & 1 deletion src/components/header/DatePicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,12 @@ export default defineComponent({
user-select: none;
border: var(--qalendar-border-gray-thin);
@include mixins.dark-mode {
color: var(--qalendar-dark-mode-text-hint);
background-color: var(--qalendar-dark-mode-lightly-elevated-surface);
border-color: transparent;
}
.qalendar-is-small & {
border: 0;
}
Expand Down Expand Up @@ -502,6 +508,12 @@ export default defineComponent({
min-width: 250px;
box-shadow: 0 2px 4px rgb(240 236 236 / 76%);
@include mixins.dark-mode {
background-color: var(--qalendar-dark-mode-elevated-surface);
border-color: transparent;
box-shadow: 0 2px 4px rgb(0 0 0 / 10%);
}
&.is-in-qalendar {
top: calc(100% - 1px);
position: absolute;
Expand Down Expand Up @@ -535,6 +547,10 @@ export default defineComponent({
transition: var(--qalendar-text-transition);
color: #131313;
@include mixins.dark-mode {
color: var(--qalendar-dark-mode-text-hint);
}
@include mixins.hover {
color: var(--qalendar-blue);
cursor: pointer;
Expand Down Expand Up @@ -604,7 +620,7 @@ export default defineComponent({
&.has-day {
@include mixins.hover {
background-color: var(--qalendar-light-gray);
background-color: var(--qalendar-option-hover);
}
}
Expand Down
20 changes: 19 additions & 1 deletion src/components/header/Header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ export default defineComponent({
grid-gap: var(--qalendar-spacing);
}
@include mixins.dark-mode {
color: var(--qalendar-dark-mode-text-hint);
}
&__period {
display: flex;
flex-wrap: wrap;
Expand Down Expand Up @@ -264,13 +268,22 @@ export default defineComponent({
cursor: pointer;
border: var(--qalendar-border-gray-thin);
@include mixins.dark-mode {
border-color: transparent;
}
.calendar-header__mode-value {
padding: 0 var(--qalendar-spacing);
width: 100%;
height: 100%;
display: flex;
align-items: center;
user-select: none;
border-radius: 4px;
@include mixins.dark-mode {
background-color: var(--qalendar-dark-mode-lightly-elevated-surface);
}
}
.calendar-header__mode-options {
Expand All @@ -282,11 +295,16 @@ export default defineComponent({
border: var(--qalendar-border-gray-thin);
background-color: #fff;
@include mixins.dark-mode {
border-color: transparent;
background-color: var(--qalendar-dark-mode-elevated-surface);
}
.calendar-header__mode-option {
padding: var(--qalendar-spacing-half) var(--qalendar-spacing);
@include mixins.hover {
background-color: var(--qalendar-light-gray);
background-color: var(--qalendar-option-hover);
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/components/month/Day.vue
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ export default defineComponent({
</script>

<style lang="scss" scoped>
@use '../../styles/_mixins.scss' as mixins;
@mixin day-base {
height: 100%;
flex: 1;
Expand All @@ -206,6 +208,10 @@ export default defineComponent({
align-items: center;
border-right: var(--qalendar-border-gray-thin);
border-bottom: var(--qalendar-border-gray-thin);
@include mixins.dark-mode {
border-color: var(--qalendar-dark-mode-line-color);
}
}
.calendar-month__weekday {
Expand Down
4 changes: 4 additions & 0 deletions src/components/month/Event.vue
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,10 @@ export default defineComponent({
@include mixins.hover {
background-color: var(--qalendar-light-gray);
@include mixins.hover {
background-color: var(--qalendar-option-hover);
}
}
.calendar-month__event-color {
Expand Down
Loading

0 comments on commit 7d42539

Please sign in to comment.