-
Notifications
You must be signed in to change notification settings - Fork 14
Styling
In order to easily style for light/dark theme, several color variables have been defined in style/theme.scss (link to come).
The dark UI is loosely following the Material Design principles.
At the time of writing, the variables defined include colors for:
- background
- overlay level 1
- overlay level 2
- overlay level 3
- overlay level 4
- text
- dark text
- light text
- hover background (semantic-ui)
These variables have definitions for light theme, with each level alternating between white and the gray-blue color, and dark theme, with each level being about 5% brighter than the previous.
In general for dark mode, each additional "card" that is overlayed should be a bit brighter than the the background color of whatever it is sitting on to show that it is higher. Also, hover effects make the background a level brighter Here is an example with the search page.
- background: the background
- overlay1: search hit items, search bar and search popup card
- overlay2: prerequisite, restrictions, and grade dist cards
- overlay3: grade dist dropdowns
- overlay4: hovered grade dist dropdown items
Additionally, I have made the border color of the search bar overlay2 since it seemed appealing to have it a level brighter than search bar (which is overlay1). For stuff like border colors, there is less of a clear-cut guide and mostly just do what seems most appealing for that particular element. For example, I opted to make the border color for the beta popup a level darker instead.
If we look at the same page in light mode, we can see how those overlays are applied in light mode. Background is the gray-blue color, overlay1 is white, and then they alternate, so in general the same color variables are used to style for both light/dark theme simultaneously.
You may notice that the item being hovered in the dropdown differs from these guidelines. It is a light gray, the default provided by Semantic-UI, rather than the gray-blue that would be expected with overlay4. Details regarding this exception are covered in the next section.
There may be scenarios in which the overlay color of the dark theme does not match the appropriate overlay color of the light theme. This is especially true when dealing with modals and with components/classes from Bootstrap or Semantic-UI. In this scenario, you will need to deal with that manually using the [data-theme='dark']
selector.
The body tag of the website has an attribute data-theme
. This will be set to the current theme i.e.
<body data-theme="dark">
You can make use of this in css by writing selectors that only apply for dark theme.
For example, this sets the styling for dark theme on the search bar.
component/SearchModule/SearchModule.scss
[data-theme='dark'] {
.search-bar,
.search-bar:focus,
.search-module .input-group-text {
color: var(--text);
background-color: var(--overlay1);
border-color: var(--overlay2);
}
.search-bar:focus {
border-color: #80bdff;
}
}
The [data-theme='dark'] selector will select any element with the attribute, which in our case is the body (if dark mode is enabled). Its use is necessary as to not overwrite the text and border color on light theme that bootstrap provides for inputs by default, since it does not match the color variables we have defined.
Some default Bootstrap and Semantic-UI overrides for dark mode have been defined already in theme.scss. However, you will need to care to make sure that the overlay level is corrected for wherever the element is used.
For example, the transfer courses modal on the roadmap page is overlay2 and so are its containing inputs. Overlay2 is the default override in theme.scss for Bootstrap's .form-control
class, which styles inputs, dropdowns, and other form elements.
The add review modal, however, has separate cards/sections that are overlay3. This required the containg form controls to also be overlay3 in order to stay consistent with our styling (in light theme the modal is gray-blue, and both the form-controls & their containing sections are white).
Implementation:
component/ReviewForm/ReviewForm.scss
[data-theme='dark'] {
.review-form {
.form-control, .form-control:focus {
background-color: var(--overlay3);
}
}
}
There are some components which have colors that are not styled using CSS. An example of this is the log in/log out button. It is a Bootstrap component which has its color defined by the variant property. By default we have this set to "light" but we want the dark variant for dark mode. To accomplish this, we make use of the ThemeContext.
The ThemeContext provider is defined in App.tsx. The opening tag looks like
<ThemeContext.Provider value={{ darkMode: darkMode, toggleTheme: toggleTheme }} >
Basically, theme context has a value that it provides which can be accessed by child components. To use the value, you can use one of two methods.
useContext is a hook which will work with any function component. You include the following line in the component and now you have access to the darkMode boolean to use for conditionally defining properties.
const { darkMode } = useContext(ThemeContext);
For example, the login button. This is the easiest way of using the theme context.
<Button variant={darkMode ? 'dark' : 'light'}>
An alternative method is the ThemeContext.Consumer tag. This is a tag you include within a component. The body of the tag will be a function with the value as the parameter and the return being the component(s) to render which depend on that value. An example is the Grade Distribution Chart. By default, the legends and tick text is a dark gray which is difficult to see in dark mode. In order to change the color, we must use the theme property for the chart. This again requires us to use ThemeContext. Normally, we would use useContext since it's easier. However, it is hook so it can only be used with function components. The Chart component happens to be a class component for whatever reason. It's implementation is as follows:
<ThemeContext.Consumer>
{({ darkMode }) =>
<ResponsiveBar
data={this.getClassData()}
keys={['A', 'B', 'C', 'D', 'F', 'P', 'NP']}
indexBy='label'
margin={{
top: 50,
right: 30,
bottom: 50,
left: 30
}}
layout='vertical'
axisBottom={{
tickSize: 10,
tickPadding: 5,
tickRotation: 0,
legend: 'Grade',
legendPosition: 'middle',
legendOffset: 36
}}
enableLabel={false}
colors={getColor}
theme={this.getTheme(darkMode)}
tooltip={this.styleTooltip}
/>
}
</ThemeContext.Consumer>
getTheme returns a theme with the appropriate colors based on whether or not dark mode is enabled.
getTheme = (darkMode: boolean) => {
return {
tooltip: {
container: {
background: 'rgba(0,0,0,.87)',
color: '#ffffff',
fontSize: '1.2rem',
outline: 'none',
margin: 0,
}
},
axis: {
ticks: {
text: {
fill: darkMode ? '#eee' : '#333'
}
},
legend: {
text: {
fill: darkMode ? '#eee' : '#333'
}
}
}
}
}