1
0
mirror of https://github.com/kognise/water.css.git synced 2025-01-17 19:58:13 +01:00

Merge pull request #191 from kognise/new-file-structure

[WIP] New File Structure + Version Picker
This commit is contained in:
Kognise 2020-05-29 22:21:31 -05:00 committed by GitHub
commit 6d66c9c05a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 249 additions and 537 deletions

View File

@ -31,6 +31,7 @@
"classPropertiesAllowed": false
}
],
"func-style": ["error", "expression"]
"func-style": ["error", "expression"],
"arrow-parens": ["error", "always"]
}
}

1
.prettierignore Normal file
View File

@ -0,0 +1 @@
*

View File

@ -26,18 +26,23 @@
## Why?
I commonly make quick demo pages or websites with simple content. For these, I don't want to spend time styling them but don't like the ugliness of the default styles.
Water.css is a CSS framework that doesn't require any classes. You just include it in your `<head>` and forget about it, while it silently makes everything nicer.
## Who?
You might want to use Water.css if you're making a simple static page or demo website that you don't want to spend time styling.
You probably don't want to use it for a production app or something that is more than a simple document. Rule of thumb: if your site has a navbar, don't use Water.css. It's just not meant for that kind of content.
Although it originally wasn't built for more complex websites, many developers have used Water.css as a base stylesheet and creatively applied custom styles to build out an entire app. Nothing is stopping you from doing the same!
## How?
Just stick this in your `<head>`:
### 🌙/☀ Automatic Theme:
`<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/dist/water.min.css">`
### 🌙 Dark Theme:
`<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/dist/dark.min.css">`
@ -54,34 +59,36 @@ Just stick this in your `<head>`:
#### Enforce a theme and ignore `(prefers-color-scheme)`
For the main versions, `dark` or `light` is only treated as a _default theme_: if a user has a system-wide preference for either dark or light mode on their device, `water.css` will respect this. If you want to avoid this behavior and enforce dark or light theme, append `.standalone` to the theme prefix, e.g. `dark.standalone.min.css`.
For the main `water.css` file, dark is only treated as a _default theme_: if a user has a preference for either dark or light mode on their device, the stylesheet will respect this. This detection is made possible through a recent CSS media query called `prefers-color-scheme`. If you want to avoid this behavior and enforce dark or light theme, use either `dark.css` or `light.css`.
#### Want to support Internet Explorer?
Sure, just extend the theme prefix with `-legacy`, e.g. `dark-legacy.min.css`.
Be aware that these versions **do not support** [runtime theming](#theming) as they use hard coded values rather than variables. Additionally, if you use a legacy version that is not standalone, we recommend [you add the respective preload tags to improve load times](https://watercss.kognise.dev/?legacy#installation).
All three distributions of Water.css support Internet Explorer 11, but the main `water.css` file **doesn't respect the user's color scheme** and will be locked to dark mode due to lack of `prefers-color-scheme` support.
Be aware that IE also doesn't support [runtime theming](#theming), and fixed fallback values will be used. If you want to override the Water.css theme, we recommend that you [compile your own theme](#compiling-your-own-theme).
#### Unminified builds
All versions are also available as unminified stylesheets, which can be handy during development.
All versions are also available as unminified stylesheets, which can be handy during development.
Simply remove the `.min` from the file name.
## Theming
Do you want to make some adjustments or build your own theme completely different from the official dark or light themes? Since Water.css is built with CSS variables this is super easy to do!
Do you want to make some adjustments or build your own theme completely different from the official dark or light themes? Since Water.css is built with CSS variables this is super easy to do!
You can find a full list of the variables used at [**src/variables-\*.css**](https://github.com/kognise/water.css/tree/master/src/variables-dark.css).
### Runtime theming
> ⚠ If you use a version with support for legacy browsers like Internet Explorer, skip to [Compiling your own theme](#compiling-your-own-theme)!
Water.css uses Custom Properties (_"CSS variables"_) to define its base styles such as colors. These can be changed and overwritten right in the browser.
Water.css uses Custom Properties (_"CSS variables"_) to define its base styles such as colors. These can be changed and overwritten right in the browser.
Because of this, you can simply add your own stylesheet to the page and set your own CSS variables there. As long as your stylesheet comes after Water.css in the HTML, your values will override the default ones and your theme is applied!
This short example will use Water.css, but color all links red:
```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/water.css@2/dist/dark.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/water.css@2/dist/water.min.css" />
<style>
:root {
--links: red;
@ -89,17 +96,17 @@ This short example will use Water.css, but color all links red:
</style>
```
If you want to change a value for dark or light mode only, use a media query like so:
If you want to change a value for dark or light mode only, use a media query like this:
```html
<style>
:root {
--links: blue; /* Always applied */
--links: yellow; /* Always applied */
}
@media (prefers-color-scheme: dark) {
@media (prefers-color-scheme: light) {
:root {
--links: yellow; /* Only applied in dark mode (overrides previous declarations while applied) */
--links: blue; /* Only applied in light mode (overrides yellow) */
}
}
</style>
@ -115,15 +122,6 @@ If you are targeting browsers without support for CSS Custom Properties such as
- Run `yarn build` to compile the CSS files
- Use the compiled files in the `dist/` directory on your site
When making your changes, we recommend you don't change the values set by Water.css directly, instead simply add your own variable declarations:
```css
/* ⬇ Add this block! */
:root {
/* Your variable declarations, overriding previous ones */
}
```
You also might want to check out the [Contributing Guide](https://github.com/kognise/water.css/tree/master/.github/CONTRIBUTING.md) as it contains further information about the build setup.
## Contributing

View File

@ -10,7 +10,6 @@
content="A drop-in collection of CSS styles to make simple websites like this just a little bit nicer."
/>
<!-- Icons generated with https://realfavicongenerator.net -->
<link rel="apple-touch-icon" sizes="180x180" href="./icons/apple-touch-icon.png" />
<link rel="icon" id="icon-16" type="image/png" sizes="16x16" href="./icons/favicon-16x16.png" />
<link rel="icon" id="icon-32" type="image/png" sizes="32x32" href="./icons/favicon-32x32.png" />
@ -24,17 +23,13 @@
<meta name="theme-color" content="#ffffff" />
<!-- Startup styles of water.css, so styles don't have to wait until JS is loaded -->
<link rel="preload" as="style" href="./water.css/dark-legacy.standalone.min.css" />
<link
rel="preload"
as="style"
media="(prefers-color-scheme: dark)"
href="./water.css/dark-legacy.standalone.min.css"
/>
<link rel="stylesheet" id="js-startup-stylesheet" href="./water.css/dark-legacy.min.css" />
<link rel="preload" as="style" href="./water.css/dark.min.css" />
<link rel="preload" as="style" href="./water.css/light.min.css" />
<link rel="stylesheet" id="js-startup-stylesheet" href="./water.css/water.min.css" />
<!-- Dynamic version of water.css, overwrites startup styles. JavaScript sets & updates href -->
<link rel="stylesheet" id="js-stylesheet" />
<!-- Custom styles for the documentation / version picker -->
<link rel="stylesheet" href="style.css" />
@ -48,7 +43,7 @@
/>
<meta
property="og:image"
content="https://raw.githubusercontent.com/kognise/water.css/29afc6c80a1a38123e47c8a50779faae97cc2a8b/assets/logo.png"
content="https://raw.githubusercontent.com/kognise/water.css/master/logo.png"
/>
<meta property="og:image:width" content="1154" />
<meta property="og:image:height" content="444" />
@ -62,6 +57,11 @@
}
</script>
<script>
// eslint-disable-next-line no-useless-escape
window.navigator.clipboard || document.write('<script src="https://unpkg.com/clipboard-polyfill@2.8.6/dist/clipboard-polyfill.promise.js"><\/script>')
</script>
<!-- Use bright favicons when the browser is in dark mode. -->
<script type="module">
import faviconSwitcher from 'https://unpkg.com/favicon-mode-switcher@1.0.4/dist/index.min.mjs'
@ -75,7 +75,10 @@
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-116663597-6"></script>
<script>
window.dataLayer = window.dataLayer || []
const gtag = (...args) => window.dataLayer.push(args)
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
const gtag = function () {
window.dataLayer.push(arguments)
}
gtag('js', new Date())
gtag('config', 'UA-116663597-6')
</script>
@ -87,22 +90,25 @@
<p>
Water.css is a drop-in collection of CSS styles to make simple websites like this just a
little bit nicer.
<br />
</p>
<p>
Now you can write your simple static site with nice semantic html, and Water.css will manage
the styling for you.
</p>
<div class="row">
<div>
<a href="#installation"><b>Get it already!</b></a>
<br />
<a href="https://github.com/kognise/water.css"><b>GitHub</b></a>
</div>
<a
href="https://www.producthunt.com/posts/water-css?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-water-css"
target="_blank"
>
<img
id="js-producthunt"
id="product-hunt"
src="https://api.producthunt.com/widgets/embed-image/v1/top-post-badge.svg?post_id=150490&theme=dark&period=daily"
alt="Water.css - Make your tiny website just a little nicer | Product Hunt Embed"
style="width: 250px; height: 54px;"
@ -113,7 +119,58 @@
</div>
<h2>Installation</h2>
<include src="./versionpicker.html"></include>
<div id="installation">
<header class="row">
<h3 id="link-snippet-headline">
Paste this into the <code>&lt;head&gt;</code> of your HTML:
</h3>
<button type="button" id="copy-button">
<span id="copy-button-feedback" class="emoji"></span>
Copy to clipboard
</button>
</header>
<div id="link-snippet-container">
<pre id="link-snippet-auto"><code>&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/dist/water.css"></code></pre>
<pre hidden id="link-snippet-dark"><code>&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/dist/dark.css"></code></pre>
<pre hidden id="link-snippet-light"><code>&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/dist/light.css"></code></pre>
</div>
<h3>Options</h3>
<form id="theme-form">
<input type="radio" value="auto" checked name="theme" id="theme-auto" />
<label for="theme-auto">Automatic <span class="emoji">🌙</span> / <span class="emoji"></span></label>
<input type="radio" value="dark" name="theme" id="theme-dark" />
<label for="theme-dark">Dark theme <span class="emoji">🌙</span></label>
<input type="radio" value="light" name="theme" id="theme-light" />
<label for="theme-light">Light theme <span class="emoji"></span></label>
</form>
<table id="version-info">
<caption>
<h3>Version info</h3>
</caption>
<tbody>
<tr>
<th scope="row">File</th>
<td id="table-file-name"></td>
</tr>
<tr>
<th scope="row">Size (min + gzip)</th>
<td id="table-file-size"></td>
</tr>
<tr>
<th scope="row">Theme</th>
<td id="table-theme"></td>
</tr>
</tbody>
</table>
</div>
<h2 id="goals">Goals</h2>
<ul>
@ -272,12 +329,6 @@
<a href="#">Back to top <span class="emoji"></span></a>
</footer>
<script
src="https://unpkg.com/clipboard-polyfill@2.8.0/build/clipboard-polyfill.promise.js"
defer
></script>
<script src="https://unpkg.com/@ungap/url-search-params@0.1.2/min.js" defer></script>
<script src="https://unpkg.com/vue@2.6.10/dist/vue.min.js" defer></script>
<script src="script.js" defer></script>
</body>
</html>

View File

@ -1,187 +1,79 @@
// @ts-check
/** @typedef {'dark' | 'light'} Theme */
/** @typedef {keyof typeof FILE_SIZES} FileName */
/** @typedef {'success' | 'failed'} CopyStatus */
/**
* @typedef {Object} VersionOptions Configurable options for water.css versions
* @prop {Theme} theme
* @prop {boolean} isLegacy
* @prop {boolean} isStandalone
*/
/**
* @typedef {Object} Libraries External packages exposed on `window` (loaded through `<script>`)
* @prop {typeof import('vue').default} Vue
* @prop {Clipboard} clipboard
* @prop {import('favicon-mode-switcher')} faviconModeSwitcher
*/
/**
* @typedef {Object} VueData State used by the version picker
* @prop {VersionOptions} versionOptions
* @prop {?CopyStatus} copyStatus
* @prop {?Theme} preferedColorScheme
*/
'use strict'
/** Reference to global window, but with properties for loaded libraries. */
const w = /** @type {Window & Libraries} */ (window)
const queryParams = new URLSearchParams(w.location.search)
const supportsCssVars = typeof CSS !== 'undefined' && CSS.supports('color', 'var(--clr)')
const localBase = './water.css/'
/** The base URI from where the docs page loads the CSS files. */
const DEV_BASE = './water.css/'
/** The base URI from where instructions show to load the CSS files. */
const CDN_BASE = 'https://cdn.jsdelivr.net/npm/water.css@2/dist/'
/** An object mapping the (minified + gzipped) fileSize in KB to a fileName. */
const FILE_SIZES = {
'dark.min.css': 1.4,
'dark.standalone.min.css': 1.31,
'dark-legacy.min.css': 0.177 + 1.16 + 1.15,
'dark-legacy.standalone.min.css': 1.16,
'light.min.css': 1.4,
'light.standalone.min.css': 1.3,
'light-legacy.min.css': 0.178 + 1.16 + 1.15,
'light-legacy.standalone.min.css': 1.15
const fileSizes = {
dark: 2.3,
light: 2.29,
auto: 2.96
}
/** Takes in version options and returns the respective CSS file name. */
const getFileName = (/** @type {VersionOptions} */ { theme, isLegacy, isStandalone }) => {
const legacySuffix = isLegacy ? '-legacy' : ''
const standaloneExt = isStandalone ? '.standalone' : ''
return /** @type {FileName} */ (`${theme}${legacySuffix}${standaloneExt}.min.css`)
const themeForm = document.getElementById('theme-form')
const stylesheet = document.getElementById('js-stylesheet')
const startupStylesheet = document.getElementById('js-startup-stylesheet')
const productHunt = document.getElementById('product-hunt')
const copyButton = document.getElementById('copy-button')
const copyButtonFeedback = document.getElementById('copy-button-feedback')
const linkSnippets = [].slice.call(document.querySelectorAll('#link-snippet-container > pre'))
const table = {
fileName: document.getElementById('table-file-name'),
fileSize: document.getElementById('table-file-size'),
theme: document.getElementById('table-theme')
}
/** Takes in version options and returns the corresponding file size in KB. */
const getFileSize = (/** @type {VersionOptions} */ options) => FILE_SIZES[getFileName(options)] || 0
const prefersColorScheme = window.matchMedia('(prefers-color-scheme: light)')
/** Takes in version options and returns an HTML snippet that preloads the main stylesheet and
* conditionally preloads the alternative stylesheet (if the alternative theme is active). */
const getFilePreloadSnippet = (/** @type {VersionOptions} */ { theme, isLegacy, isStandalone }) => {
const alternativeTheme = theme === 'dark' ? 'light' : 'dark'
const alternativeFile = getFileName({ theme: alternativeTheme, isLegacy, isStandalone })
return `
<!-- Preload the required stylesheets (optional) -->
<link rel="preload" as="style" href="${CDN_BASE}${getFileName({ theme, isLegacy, isStandalone })}">
<link rel="preload" as="style" href="${CDN_BASE}${alternativeFile}" media="(prefers-color-scheme: ${alternativeTheme})">`
const updateProductHunt = (theme) => {
theme = theme || (prefersColorScheme.matches ? 'light' : 'dark')
productHunt.src = `https://api.producthunt.com/widgets/embed-image/v1/top-post-badge.svg?post_id=150490&theme=${theme}&period=daily`
}
/** Takes in version options and returns the code snippet instructing users how to load the file. */
const getFileSnippet = (/** @type {VersionOptions} */ { theme, isLegacy, isStandalone }) => {
const fileName = getFileName({ theme, isLegacy, isStandalone })
const stylesheetSnippet = `<link rel="stylesheet" href="${CDN_BASE}${fileName}">`
const updateTheme = () => {
const theme = themeForm.querySelector('input[name="theme"]:checked').value
if (!isLegacy || isStandalone) return stylesheetSnippet
const fileName = `${theme === 'auto' ? 'water' : theme}.min.css`
const localUrl = `${localBase}${fileName}`
const preloadSnippet = getFilePreloadSnippet({ theme, isLegacy, isStandalone: true })
return (preloadSnippet + '\n\n' + stylesheetSnippet).trim()
}
stylesheet.href = localUrl
/** Handles elements external to the version picker that still need to be kept
* up to date with the current version, e.g. switching images from dark to light. */
const externalElements = {
_productHunt: /** @type {HTMLImageElement} */ (document.querySelector('#js-producthunt')),
_stylesheet: /** @type {HTMLLinkElement} */ (document.querySelector('#js-stylesheet')),
_removeStartupStylesheet: () => {
const startupStylesheet = document.head.querySelector('#js-startup-stylesheet')
if (startupStylesheet) document.head.removeChild(startupStylesheet)
externalElements._stylesheet.removeEventListener('load', externalElements._removeStartupStylesheet)
},
_updateProductHunt: (/** @type {Theme} */ theme) => {
externalElements._productHunt.src = externalElements._productHunt.src.replace(/dark|light/, theme)
},
_updateStylesheet: (/** @type {FileName} */ fileName) => {
externalElements._stylesheet.href = DEV_BASE + fileName
},
for (const snippet of linkSnippets) {
snippet.hidden = snippet.id.indexOf(theme) === -1
}
/** Sets up listener to remove startup version of water.css when right one loads, then updates */
init: (/** @type {VersionOptions} */ options, /** @type {?Theme} */ preferedTheme) => {
externalElements._stylesheet.addEventListener('load', externalElements._removeStartupStylesheet)
externalElements.update(options, preferedTheme)
},
/** Takes current version + the user's prefered scheme and updates all external elements. */
update: (/** @type {VersionOptions} */ options, /** @type {?Theme} */ preferedTheme) => {
const displayedTheme = options.isStandalone ? options.theme : preferedTheme || options.theme
table.fileName.innerText = fileName
table.fileSize.innerText = `${fileSizes[theme].toFixed(2)} kb`
externalElements._updateStylesheet(getFileName(options))
externalElements._updateProductHunt(displayedTheme)
if (theme === 'auto') {
updateProductHunt()
table.theme.innerHTML = `
Respects user-defined theme settings using <a style="--links: var(--code)" href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme" target="_blank" rel="noopener"><code>prefers-color-scheme</code></a>.<br>
Light in browsers where the theme settings can't be detected.
`
} else {
updateProductHunt(theme)
table.theme.innerText = `Theme is forced to ${theme}.`
}
}
/**
* Sets up a media query for the given color scheme and runs the callback on change.
* @param {Theme} scheme
* @param {(matches: boolean) => any} queryHandler
*/
const createColorSchemeListener = (scheme, queryHandler) => {
const mediaQuery = w.matchMedia(`(prefers-color-scheme: ${scheme})`)
mediaQuery.addListener(query => queryHandler(query.matches))
queryHandler(mediaQuery.matches)
}
themeForm.addEventListener('change', updateTheme)
const themeFromParams = queryParams.get('theme')
/** @type {VersionOptions} */
const initialVersionOptions = {
theme: themeFromParams === 'dark' || themeFromParams === 'light' ? themeFromParams : 'dark',
isLegacy: queryParams.has('legacy') || !supportsCssVars,
isStandalone: queryParams.has('standalone')
}
new w.Vue({ // eslint-disable-line no-new
el: '#installation',
filters: {
capitalize: (/** @type {string} */ str) => str.charAt(0).toUpperCase() + str.slice(1)
},
/** @type {VueData} */
data: {
versionOptions: initialVersionOptions,
preferedColorScheme: null,
copyStatus: null
},
computed: {
/** @returns {boolean} */
isOverwritten () {
const { isStandalone, theme } = this.versionOptions
if (isStandalone || !this.preferedColorScheme) return false
return theme !== this.preferedColorScheme
},
/** @returns {{ fileName: string, fileSize: string, fileSnippet: string }} */
selectedVersion () {
return {
fileName: getFileName(this.versionOptions),
fileSize: getFileSize(this.versionOptions).toFixed(2),
fileSnippet: getFileSnippet(this.versionOptions)
}
}
},
created () {
createColorSchemeListener('dark', match => match && (this.preferedColorScheme = 'dark'))
createColorSchemeListener('light', match => match && (this.preferedColorScheme = 'light'))
externalElements.init(this.versionOptions, this.preferedColorScheme)
},
methods: {
getTooltipMessage (/** @type {Theme} */ theme) {
if (this.versionOptions.theme === theme && this.isOverwritten) {
return 'Your theme selection is currently overwritten by the theme setting on your device.'
} else return "Selected theme can be overwritten by theme setting on user's device."
},
copyToClipboard () {
Promise.resolve()
.then(() => w.clipboard.writeText(this.selectedVersion.fileSnippet))
.then(() => (this.copyStatus = 'success'))
.catch(() => (this.copyStatus = 'failed'))
setTimeout(() => (this.copyStatus = null), 1000)
}
},
watch: {
preferedColorScheme (/** @type {Theme} */ nextScheme) {
externalElements.update(this.versionOptions, nextScheme)
},
versionOptions: {
deep: true,
handler (/** @type {VersionOptions} */ nextOptions) {
externalElements.update(nextOptions, this.preferedColorScheme)
}
}
}
updateProductHunt()
prefersColorScheme.addListener(() => {
if (themeForm.theme.value !== 'auto') return
updateProductHunt()
})
updateTheme()
startupStylesheet.parentElement.removeChild(startupStylesheet)
copyButton.addEventListener('click', () => {
const clipboard = navigator.clipboard || window.clipboard
const theme = themeForm.querySelector('input[name="theme"]:checked').value
const snippetText = document.querySelector(`#link-snippet-${theme} code`).textContent
clipboard.writeText(snippetText)
.then(() => { copyButtonFeedback.textContent = '✔' })
.catch(() => { copyButtonFeedback.textContent = '❌' })
.then(() => setTimeout(() => { copyButtonFeedback.textContent = '' }, 1000))
})

View File

@ -2,65 +2,59 @@ html {
scroll-behavior: smooth;
}
#js-producthunt {
#product-hunt {
margin-top: 1rem;
}
.version-select {
overflow: hidden;
}
.version-select__snippet h3 {
#link-snippet-headline {
margin: 1rem 2.5rem 1rem 0;
}
/* Make the ✔ / ❌ Emoji appear next to the button */
.version-select__snippet__btn {
/* Make the feedback Emoji appear next to the button */
#copy-button {
position: relative;
margin: 0 2px 0 auto;
margin-right: 2px;
overflow: visible;
}
.version-select__snippet__btn span {
#copy-button-feedback {
position: absolute;
left: -2rem;
display: inline-block;
transform: scale(1.3);
}
.version-select__options > label {
white-space: pre;
}
.version-select__options > label:not(:last-of-type) {
margin-right: 1rem;
}
.version-select__options__additional,
.version-select__options__additional > *:not(summary) {
display: block;
margin: 1rem 0;
}
.version-select__options__additional summary {
outline: none;
cursor: pointer;
font-weight: 600;
}
.version-select__options__additional summary:hover span,
.version-select__options__additional summary:focus span {
text-decoration: underline;
#link-snippet-container {
overflow: hidden;
display: grid;
display: -ms-grid;
-ms-grid-columns: 1fr;
}
.version-select__info caption {
text-align: left;
#link-snippet-container > pre {
overflow: auto;
grid-column: 1;
grid-row: 1;
transition: transform 220ms cubic-bezier(0.175, 0.885, 0.32, 1) 220ms;
}
.version-select__info th {
border: none;
vertical-align: top;
#link-snippet-container > pre[hidden] {
display: block;
visibility: hidden;
transform: scale(0);
transition: transform 220ms cubic-bezier(0.175, 0.885, 0.32, 1), visibility 0ms 220ms;
}
#theme-form > label:not(:last-of-type) {
margin-right: 1rem;
}
#version-info th {
width: 35%;
}
body > footer {
padding-top: 2rem;
display: flex;
justify-content: flex-end;
align-items: flex-end;
text-align: right;
}
.row {
@ -69,44 +63,23 @@ body > footer {
align-items: center;
justify-content: space-between;
}
.emoji {
/* Use proper Emoji instead of plain Unicode chars */
font-family: 'Segoe UI Emoji', 'Apple Color Emoji', 'Noto Color Emoji', system-ui, -apple-system,
BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans,
Helvetica Neue, sans-serif;
}
.opacity-4 {
opacity: 0.4;
}
.opacity-7 {
opacity: 0.76;
}
.tooltip {
position: relative;
cursor: help;
}
.tooltip::after {
content: '';
position: absolute;
left: 0;
bottom: -4px;
width: 100%;
border-width: 0 0 1.5px;
border-style: dotted;
}
/* For the bounce transitions of code snippet and copy success Emoji */
.v-enter,
.v-leave-to {
transform: scale(0) !important;
}
.v-enter-active,
.v-leave-active {
transition: transform 220ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
[v-cloak] > * {
display: none;
}
[v-cloak]::after {
content: 'Version picker is loading...';
font-family:
'Segoe UI Emoji',
'Apple Color Emoji',
'Noto Color Emoji',
system-ui,
-apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif;
}

View File

@ -1,119 +0,0 @@
<!-- Start interactive version picker -->
<div id="installation" class="version-select" v-cloak>
<header class="row version-select__snippet">
<h3>Paste this into the <code>&lt;head&gt;</code> of your HTML:</h3>
<button type="button" class="version-select__snippet__btn" @click="copyToClipboard">
<transition>
<span v-if="copyStatus === 'success'" class="emoji"></span>
<span v-else-if="copyStatus === 'failed'" class="emoji"></span>
</transition>
Copy to clipboard
</button>
</header>
<transition mode="out-in">
<pre :key="selectedVersion.fileSnippet"><code>{{selectedVersion.fileSnippet}}</code></pre>
</transition>
<h3>Options</h3>
<form action="./" method="post" @submit.prevent class="version-select__options">
<label>
<input type="radio" value="dark" checked name="theme" v-model="versionOptions.theme" />
Dark theme&nbsp;<span class="emoji">🌙</span>
<span
v-if="!versionOptions.isStandalone"
class="tooltip emoji opacity-4"
:class="{'opacity-7': versionOptions.theme === 'dark' && isOverwritten}"
:title="getTooltipMessage('dark')"
>
/ ☀</span
>
</label>
<label>
<input type="radio" value="light" name="theme" v-model="versionOptions.theme" />
Light theme&nbsp;<span class="emoji"></span>
<span
v-if="!versionOptions.isStandalone"
class="tooltip emoji opacity-4"
:class="{'opacity-7': versionOptions.theme === 'light' && isOverwritten}"
:title="getTooltipMessage('light')"
>
/ 🌙</span
>
</label>
<details class="version-select__options__additional">
<summary><span>Additional options</span></summary>
<label>
<input type="checkbox" checked v-model="versionOptions.isStandalone" />
Enforce theme? (ignore <code>prefers-color-scheme</code>)
</label>
<label>
<input type="checkbox" v-model="versionOptions.isLegacy" />
Support Internet Explorer and other legacy browsers?
</label>
</details>
</form>
<table class="version-select__info">
<caption>
<h3>Version info</h3>
</caption>
<tbody>
<tr>
<th scope="row">File</th>
<td>{{ selectedVersion.fileName }}</td>
</tr>
<tr>
<th scope="row">Size (min+gzip)</th>
<td>{{ selectedVersion.fileSize }}KB</td>
</tr>
<tr>
<th scope="row">Theme</th>
<td>
<template v-if="versionOptions.isStandalone">
<span v-if="versionOptions.theme === 'dark'" class="emoji">🌙</span>
<span v-else class="emoji"></span>
{{ versionOptions.theme | capitalize }}
</template>
<template v-else>
<span
v-if="versionOptions.theme === 'dark'"
class="emoji"
:title="getTooltipMessage('dark')"
>
🌙<span class="opacity-4" :class="{'opacity-7': isOverwritten}"> / ☀</span>
</span>
<span v-else class="emoji" :title="getTooltipMessage('light')">
<span class="opacity-4" :class="{'opacity-7': isOverwritten}"> / 🌙</span>
</span>
Defaults to {{ versionOptions.theme }}, but respects user-defined theme settings.
(detected via <code>prefers-color-scheme</code>)
<template v-if="preferedColorScheme">
<br />
<span :style="'text-decoration: ' + (isOverwritten ? 'underline' : 'none')">
Your device is currently set to {{ preferedColorScheme }} mode.
</span>
</template>
</template>
</td>
</tr>
<tr>
<th scope="row">Browser support</th>
<td>
<template v-if="versionOptions.isLegacy">
All browsers (including Internet Explorer)
</template>
<template v-else>
All current browsers (<a href="https://caniuse.com/#feat=css-variables"
>support for CSS Custom Properties</a
>)
</template>
</td>
</tr>
</tbody>
</table>
</div>
<!-- End interactive version picker -->

View File

@ -20,7 +20,7 @@ const postcssImport = require('postcss-import')
const postcssInlineSvg = require('postcss-inline-svg')
const postcssColorModFunction = require('postcss-color-mod-function').bind(null, {
/* Use `.toRGBLegacy()` as other methods can result in lots of decimals */
stringifier: color => color.toRGBLegacy()
stringifier: (color) => color.toRGBLegacy()
})
const paths = {
@ -53,73 +53,45 @@ const formatByteMessage = (source, data) => {
}
const style = () => {
const isLegacy = (path) => /legacy/.test(path)
const excludeModern = filter(file => isLegacy(file.path), { restore: true })
const excludeLegacy = filter(file => !isLegacy(file.path), { restore: true })
// Don't inline minified versions, so builds can lazily import them at runtime
const cssImportOptions = { filter: (path) => !/\.min/.test(path) }
const startDiff = () => bytediff.start()
const endDiff = (source) => bytediff.stop((data) => formatByteMessage(source, data))
return (
gulp
.src(paths.styles.src)
// Add sourcemaps
.pipe(sourcemaps.init())
// Resolve imports, calculated colors and inlined SVG files
.pipe(postcss([postcssImport(cssImportOptions), postcssColorModFunction(), postcssInlineSvg()]))
// * Process legacy builds *
.pipe(excludeModern)
// Inline variable values so CSS works in legacy browsers
.pipe(postcss([postcssCssVariables()]))
// Calculate size before autoprefixing
.pipe(bytediff.start())
// autoprefix
.pipe(postcss([autoprefixer({
env: 'legacy'
})]))
// Write the amount gained by autoprefixing
.pipe(bytediff.stop(data => formatByteMessage('autoprefixer', data)))
.pipe(excludeModern.restore)
.pipe(startDiff())
.pipe(postcss([postcssCssVariables({ preserve: true })]))
.pipe(endDiff('css variables'))
// * Process modern builds *
.pipe(excludeLegacy)
// Calculate size before autoprefixing
.pipe(bytediff.start())
// autoprefix modern builds
.pipe(postcss([autoprefixer({
env: 'modern'
})]))
// Write the amount gained by autoprefixing
.pipe(bytediff.stop(data => formatByteMessage('autoprefixer', data)))
.pipe(excludeLegacy.restore)
.pipe(startDiff())
.pipe(postcss([autoprefixer({ env: 'legacy' })]))
.pipe(endDiff('autoprefixer'))
// Write the sourcemaps after making pre-minified changes
.pipe(sourcemaps.write('.'))
// Flatten output so files end up in dist/*, not dist/builds/*
.pipe(flatten())
// Write pre-minified styles
.pipe(flatten()) // Put files in dist/*, not dist/builds/*
.pipe(gulp.dest(paths.styles.dest))
// Remove sourcemaps from the pipeline, only keep css
.pipe(filter('**/*.css'))
// Calculate size before minifying
.pipe(bytediff.start())
// Minify using cssnano, use extra-low precision while minifying inline SVGs
.pipe(filter('**/*.css')) // Remove sourcemaps from the pipeline
// <minifying>
.pipe(startDiff())
.pipe(postcss([cssnano({ preset: ['default', { svgo: { floatPrecision: 0 } }] })]))
// Write the amount saved by minifying
.pipe(bytediff.stop(data => formatByteMessage('cssnano', data)))
// Rename the files have the .min suffix
.pipe(endDiff('minification'))
.pipe(rename({ suffix: '.min' }))
// Write the sourcemaps after making all changes
// </minifying>
.pipe(sourcemaps.write('.'))
// Write the minified files
.pipe(gulp.dest(paths.styles.dest))
// Output files to docs directory so documentation site can use them
.pipe(gulp.dest(paths.docs.dest + '/water.css'))
// Final size report including gzipped sizes
.pipe(filter('**/*.css')) // Remove sourcemaps from the pipeline
.pipe(sizereport({ gzip: true, total: false, title: 'SIZE REPORT' }))
// Stream any changes to browserSync
.pipe(browserSync.stream())
)
}

View File

@ -7,7 +7,7 @@
"build": "gulp build",
"dev": "gulp watch",
"lint:js": "eslint **/*.js **/*.html gulpfile.js --fix",
"lint:css": "stylelint src/**/*.css --fix",
"lint:css": "stylelint src/**/*.css docs/style.css --fix",
"lint": "yarn lint:js && yarn lint:css"
},
"repository": {
@ -71,24 +71,10 @@
"browserslist": {
"legacy": [
"defaults AND not android 4.4.3"
],
"modern": [
"Edge > 16",
"Firefox > 31",
"Chrome > 49",
"Safari > 9.1",
"Opera > 36",
"ios_saf > 9.3",
"Android > 76",
"OperaMobile > 46",
"ChromeAndroid > 76",
"FirefoxAndroid > 68",
"UCAndroid > 12.12",
"Samsung > 5"
]
},
"files": [
"dist/*.css",
"LICENSE.md"
]
}
}

View File

@ -1,9 +0,0 @@
/**
* Dark-themed version for legacy browsers:
* Loads the compiled, standalone version of the dark theme,
* but overrides it with the compiled, standalone version of the light theme
* if a system-wide theme preference is set on the user's device.
*/
@import url('./dark-legacy.standalone.min.css');
@import url('./light-legacy.standalone.min.css') (prefers-color-scheme: light);

View File

@ -1,8 +0,0 @@
/**
* Standalone dark-themed version for legacy browsers.
* Includes dark variables and core, compiled at build time so the final output
* will only include regular CSS, no variables.
*/
@import '../variables-dark.css';
@import '../parts/_core.css';

8
src/builds/dark.css Normal file → Executable file
View File

@ -1,12 +1,6 @@
/**
* Dark-themed version:
* uses dark theme by default but switches to light theme
* if a system-wide theme preference is set on the user's device.
*
* Variables will remain uncompiled so the theme can update dynamically
* at runtime in the browser.
* Forced dark theme version
*/
@import '../variables-dark.css';
@import '../variables-light.css' (prefers-color-scheme: light);
@import '../parts/_core.css';

View File

@ -1,8 +0,0 @@
/**
* Standalone dark-themed version.
* Includes dark variables and core, left as CSS variables
* so the theming can be adjusted at runtime.
*/
@import '../variables-dark.css';
@import '../parts/_core.css';

View File

@ -1,9 +0,0 @@
/**
* Light-themed version for legacy browsers:
* Loads the compiled, standalone version of the light theme at runtime,
* but overrides it with the compiled, standalone version of the dark theme
* if a system-wide theme preference is set on the user's device.
*/
@import url('./light-legacy.standalone.min.css');
@import url('./dark-legacy.standalone.min.css') (prefers-color-scheme: dark);

View File

@ -1,8 +0,0 @@
/**
* Standalone light-themed version for legacy browsers.
* Includes light variables and core, compiled at build time so the final output
* will only include regular CSS, no variables.
*/
@import '../variables-light.css';
@import '../parts/_core.css';

8
src/builds/light.css Normal file → Executable file
View File

@ -1,12 +1,6 @@
/**
* Light-themed version:
* uses light theme by default but switches to dark theme
* if a system-wide theme preference is set on the user's device.
*
* Variables will remain uncompiled so the theme can update dynamically
* at runtime in the browser.
* Forced light theme version
*/
@import '../variables-light.css';
@import '../variables-dark.css' (prefers-color-scheme: dark);
@import '../parts/_core.css';

View File

@ -1,8 +0,0 @@
/**
* Standalone light-themed version.
* Includes light variables and core, left as CSS variables
* so the theming can be adjusted at runtime.
*/
@import '../variables-light.css';
@import '../parts/_core.css';

9
src/builds/water.css Normal file
View File

@ -0,0 +1,9 @@
/**
* Automatic version:
* Uses light theme by default but switches to dark theme
* if a system-wide theme preference is set on the user's device.
*/
@import '../variables-light.css';
@import '../variables-dark.css' (prefers-color-scheme: dark);
@import '../parts/_core.css';

View File

@ -29,14 +29,24 @@ select {
outline: none;
}
input,
label {
input[type='checkbox'],
input[type='radio'] {
height: 1em;
width: 1em;
}
input[type='radio'] {
border-radius: 100%;
}
input {
vertical-align: top;
}
label {
display: inline-block;
vertical-align: middle;
margin-bottom: 4px;
display: inline-block;
}
input:not([type='checkbox']):not([type='radio']),