├── .npmrc ├── static ├── favicon │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── apple-touch-icon.png │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── site.webmanifest │ └── about.txt ├── fonts │ └── firacode.ttf └── images │ └── san-felipe-del-morro-castle.jpg ├── .gitignore ├── .prettierrc ├── src ├── lib │ ├── index.js │ ├── site │ │ └── san-felipe-del-morro-castle.jpg │ └── components │ │ ├── Picture.svelte │ │ └── Image.svelte ├── routes │ ├── __layout.svelte │ └── index.svelte.md ├── app.html └── app.css ├── jsconfig.json ├── .eslintrc.cjs ├── svelte.config.js ├── CHANGELOG.md ├── LICENSE ├── mdsvex.config.js ├── package.json └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /static/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawyerclick/svelte-lazy-loader/HEAD/static/favicon/favicon.ico -------------------------------------------------------------------------------- /static/fonts/firacode.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawyerclick/svelte-lazy-loader/HEAD/static/fonts/firacode.ttf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100 6 | } 7 | -------------------------------------------------------------------------------- /static/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawyerclick/svelte-lazy-loader/HEAD/static/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /static/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawyerclick/svelte-lazy-loader/HEAD/static/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /static/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawyerclick/svelte-lazy-loader/HEAD/static/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /static/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawyerclick/svelte-lazy-loader/HEAD/static/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /static/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawyerclick/svelte-lazy-loader/HEAD/static/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /src/lib/index.js: -------------------------------------------------------------------------------- 1 | export { default as Image } from './components/Image.svelte'; 2 | 3 | export { default as Picture } from './components/Picture.svelte'; 4 | -------------------------------------------------------------------------------- /src/lib/site/san-felipe-del-morro-castle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawyerclick/svelte-lazy-loader/HEAD/src/lib/site/san-felipe-del-morro-castle.jpg -------------------------------------------------------------------------------- /static/images/san-felipe-del-morro-castle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sawyerclick/svelte-lazy-loader/HEAD/static/images/san-felipe-del-morro-castle.jpg -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "$lib": ["src/lib"], 6 | "$lib/*": ["src/lib/*"] 7 | } 8 | }, 9 | "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] 10 | } 11 | -------------------------------------------------------------------------------- /static/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"favicon/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"favicon/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['eslint:recommended', 'prettier'], 4 | plugins: ['svelte3'], 5 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 6 | parserOptions: { 7 | sourceType: 'module', 8 | ecmaVersion: 2020 9 | }, 10 | env: { 11 | browser: true, 12 | es2017: true, 13 | node: true 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /static/favicon/about.txt: -------------------------------------------------------------------------------- 1 | This favicon was generated using the following graphics from Twitter Twemoji: 2 | 3 | - Graphics Title: 1f9a5.svg 4 | - Graphics Author: Copyright 2020 Twitter, Inc and other contributors (https://github.com/twitter/twemoji) 5 | - Graphics Source: https://github.com/twitter/twemoji/blob/master/assets/svg/1f9a5.svg 6 | - Graphics License: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/) 7 | -------------------------------------------------------------------------------- /src/routes/__layout.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | {title} 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | %svelte.head% 13 | 14 | 15 |
16 |
%svelte.body%
17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { mdsvex } from 'mdsvex'; 3 | import mdsvexConfig from './mdsvex.config.js'; 4 | import adapter from '@sveltejs/adapter-static'; 5 | import { imagetools } from 'vite-imagetools'; 6 | 7 | /** @type {import('@sveltejs/kit').Config} */ 8 | const config = { 9 | extensions: ['.svelte', ...mdsvexConfig.extensions], 10 | preprocess: [mdsvex(mdsvexConfig)], 11 | kit: { 12 | adapter: adapter(), 13 | package: { 14 | files: (id) => !id.startsWith('site/') 15 | }, 16 | vite: { 17 | plugins: [imagetools()], 18 | resolve: { 19 | alias: { 20 | 'svelte-lazy-loader': path.resolve('src/lib') 21 | } 22 | } 23 | } 24 | } 25 | }; 26 | 27 | export default config; 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # svelte-lazy-loader changelog 2 | 3 | ## 0.0.12 4 | 5 | - Internal: Remove example image from package 6 | 7 | ## 0.0.11 8 | 9 | - Picture: Add picture component 10 | - Image: Generalize internal load logic, forward load event, adjust rootMargin to '200px 200px', consolidate load CSS transition, fix some prop types typos 11 | - Docs: Fix typos, update props, add Picture component with example 12 | 13 | ## 0.0.10 14 | 15 | - Docs: Fix a few typos 16 | 17 | ## 0.0.9 18 | 19 | - Docs: Fix a few typos 20 | 21 | ## 0.0.8 22 | 23 | - Docs: Fix component case 24 | 25 | ## 0.0.4 26 | 27 | - Image: Default `src` to `placeholder` 28 | - Image: Default `srcset` to `src` 29 | - Image: Default `draggable` to true to match default browser behavior 30 | - Image: Remove unnecessary CSS 31 | 32 | ## 0.0.3 33 | 34 | - Various 35 | 36 | ## 0.0.2 37 | 38 | - Fix Image export 39 | 40 | ## 0.0.1 41 | 42 | - First release with Image component -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 [Sawyer Click](https://github.com/sawyer-click/svelte-lazy-loader/graphs/contributors) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /mdsvex.config.js: -------------------------------------------------------------------------------- 1 | import urls from 'rehype-urls'; 2 | import slug from 'rehype-slug'; 3 | import autoLinkHeadings from 'rehype-autolink-headings'; 4 | import { visit } from 'unist-util-visit'; 5 | import { h } from 'hastscript'; 6 | 7 | /** 8 | * Modified from https://github.com/josestg/rehype-figure 9 | */ 10 | function figure() { 11 | function buildFigure({ properties }) { 12 | const figure = h('figure', null, [ 13 | h('img', { ...properties }), 14 | properties.title ? h('figcaption', properties.title) : '' 15 | ]); 16 | return figure; 17 | } 18 | 19 | return function (tree) { 20 | visit(tree, { tagName: 'p' }, (node, index) => { 21 | const images = node.children 22 | .filter((n) => n.tagName === 'img') 23 | .map((img) => buildFigure(img)); 24 | 25 | if (images.length === 0) return; 26 | 27 | tree.children[index] = 28 | images.length === 1 ? images[0] : (tree.children[index] = h('div', null, images)); 29 | }); 30 | }; 31 | } 32 | 33 | function processUrl(url, node) { 34 | if (node.tagName === 'a') { 35 | node.properties.class = 'text-link'; 36 | 37 | if (!url.href.startsWith('/') && !url.href.startsWith('#')) { 38 | // Open external links in new tab 39 | node.properties.target = '_blank'; 40 | // Fix a security concern with offsite links 41 | // See: https://web.dev/external-anchors-use-rel-noopener/ 42 | node.properties.rel = 'noopener'; 43 | } 44 | } 45 | } 46 | 47 | const config = { 48 | extensions: ['.svelte.md'], 49 | layout: "./src/routes/__layout.svelte", 50 | smartypants: true, 51 | rehypePlugins: [ 52 | figure, // convert images into
elements 53 | [urls, processUrl], // adds rel and target to elements 54 | slug, // adds slug to

-

elements 55 | [autoLinkHeadings, { behavior: 'wrap' }], // adds a around slugged

-

elements 56 | ] 57 | }; 58 | 59 | export default config; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-lazy-loader", 3 | "version": "1.0.0", 4 | "description": "A Svelte component library for effortlessly lazy-loading media", 5 | "author": { 6 | "name": "Sawyer Click", 7 | "email": "sawyerclick@gmail.com", 8 | "url": "https://sawyer.codes/" 9 | }, 10 | "license": "MIT", 11 | "homepage": "svelte-lazy-loader.netlify.app", 12 | "keywords": [ 13 | "svelte", 14 | "sveltekit", 15 | "lazyload", 16 | "image" 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/sawyerclick/svelte-lazy-loader.git" 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/sawyerclick/svelte-lazy-loader/issues" 24 | }, 25 | "scripts": { 26 | "dev": "svelte-kit dev", 27 | "build": "svelte-kit build", 28 | "package": "svelte-kit package", 29 | "prepublishOnly": "npm run release && exit 1", 30 | "preview": "svelte-kit preview", 31 | "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .", 32 | "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. .", 33 | "postversion": "git push && git push --tags && npm run release", 34 | "release": "npm run package && cd package && npm publish" 35 | }, 36 | "devDependencies": { 37 | "@sveltejs/adapter-static": "^1.0.0-next.28", 38 | "@sveltejs/kit": "next", 39 | "eslint": "^7.32.0", 40 | "eslint-config-prettier": "^8.3.0", 41 | "eslint-plugin-svelte3": "^3.2.1", 42 | "hastscript": "^7.0.2", 43 | "mdsvex": "^0.10.5", 44 | "prettier": "^2.5.1", 45 | "prettier-plugin-svelte": "^2.5.0", 46 | "rehype-autolink-headings": "^6.1.1", 47 | "rehype-slug": "^5.0.1", 48 | "rehype-urls": "^1.1.1", 49 | "svelte": "^3.44.0", 50 | "svelte2tsx": "^0.5.5", 51 | "typescript": "^4.5.5", 52 | "unist-util-visit": "^4.1.0", 53 | "vite-imagetools": "^4.0.3" 54 | }, 55 | "type": "module" 56 | } 57 | -------------------------------------------------------------------------------- /src/lib/components/Picture.svelte: -------------------------------------------------------------------------------- 1 | 38 | 39 | 42 | 83 | 84 | 85 | 86 | 97 | 98 | 99 | 108 | -------------------------------------------------------------------------------- /src/lib/components/Image.svelte: -------------------------------------------------------------------------------- 1 | 31 | 32 | 35 | 82 | 83 | 100 | 101 | 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # svelte-lazy-loader 2 | 3 | A component library for effortlessly loading media using a shared IntersectionObserver instance if native lazy-loading doesn't exist. 4 | 5 | ## Table of Contents 6 | - [svelte-lazy-loader](#svelte-lazy-loader) 7 | - [Table of Contents](#table-of-contents) 8 | - [Installation](#installation) 9 | - [Components](#components) 10 | - [Image](#image) 11 | - [Usage](#usage) 12 | - [API](#api) 13 | - [Picture](#picture) 14 | - [Usage](#usage-1) 15 | - [API](#api-1) 16 | - [License](#license) 17 | 18 | ## Installation 19 | 20 | ```shell 21 | npm i svelte-lazy-loader 22 | ``` 23 | 24 | ## Components 25 | 26 | ### Image 27 | 28 | An expansion of the HTMLImageElement that, if a browser cannot natively lazy-load, uses a shared IntersectionObserver instance to performantly lazy load images. This components takes several native attributes and passes them through to the underlying HTMLImageElement. A few component-specific props are available to facilitate lazy-loading. 29 | 30 | The out-of-the-box implementation of this component features a blur transition effect. The CSS can be altered using [Svelte's style props](https://svelte.dev/docs#template-syntax-component-directives---style-props). 31 | 32 | #### Usage 33 | 34 | ```html 35 | 38 | 39 | A description of the image 40 | ``` 41 | 42 | #### API 43 | 44 | | Parameter | Default | Description | 45 | | -------- | ------- | ----------- | 46 | | src | `placeholder` | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/src). The path to the image. Defaults to the `{placeholder}` prop. | 47 | | srcset | `src` | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/srcset). If passed, defines which images should be presented. Defaults to the `src` prop, which defaults to the `placeholder` prop. | 48 | | loading | 'lazy' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/loading). If `lazy`, lazy-load image through native browser functionality if it exists or through IntersectionObserver if it doesn't. If `eager`, image is loaded immediately. | 49 | | alt | ' ' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/alt). A description of the image. | 50 | | draggable | false | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable). If `false`, the element can not be dragged. If `true`, the element can be dragged. | 51 | | decoding | 'async' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decoding). If `async`, decode the image asynchronously to reduce delay in presenting other content. If `sync`, decode the image synchronously for atomic presentation with other content. If `default`, default mode, which indicates no preference for the decoding mode. The browser decides what is best for the user. | 52 | | width | '100%' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/width). An integer value indicating the width of the image. | 53 | | height | '100%' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/height). An integer value indicating the height of the image. | 54 | | classes | ' ' | Additional classes to be added to the `` element. | 55 | | placeholder | 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8+fOvJAAI7AMKHxaZiQAAAABJRU5ErkJggg==' | If `loading='lazy'`, this value is the placeholder until the image is loaded. Accepts strings, including paths or base64 images such as the default. | 56 | | on:load | event | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onload). An event forwarded from the `` element when the image is loaded. | 57 | | --transition | 'filter cubic-bezier(0.4, 0, 0.2, 1) 300ms' | The CSS transition that occurs on image load. | 58 | | --filter | 'blur(2px)' | The filter to apply to the image when unloaded. Transitions out when image is loaded. | 59 | 60 | ### Picture 61 | 62 | #### Usage 63 | 64 | Similarly to the above Image component, this component uses a shared IntersectionObserver instance to performantly lazy-load images if native lazy-loading doesn't exist. Sources can be passed through a default [slot](https://svelte.dev/tutorial/slots) while `` attributes are passed as props using the API below. `` elements' `srcset` attributed should be set at `data-srcset`. 65 | 66 | Similar to above, the out-of-the-box implementation of this component features a blur transition effect. The CSS can be altered using [Svelte's style props](https://svelte.dev/docs#template-syntax-component-directives---style-props). 67 | 68 | This example uses [vite-imagetools](https://www.npmjs.com/package/vite-imagetools) create images at compile-time via [sharp](https://sharp.pixelplumbing.com/). 69 | 70 | *Note: The `srcset` attribute for `` elements should be set at data-srcset for lazyloading to work properly.* 71 | 72 | ```html 73 | 79 | 80 | 81 | {#each sources as { src, format }} 82 | 83 | {/each} 84 | 85 | ``` 86 | 87 | 88 | {#each sources as { src, format }} 89 | 90 | {/each} 91 | 92 | 93 | #### API 94 | 95 | | Parameter | Default | Description | 96 | | -------- | ------- | ----------- | 97 | | src | `placeholder` | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/src). The path to the image. Defaults to the `{placeholder}` prop. | 98 | | loading | 'lazy' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/loading). If `lazy`, lazy-load image through native browser functionality if it exists or through IntersectionObserver if it doesn't. If `eager`, image is loaded immediately. | 99 | | alt | ' ' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/alt). A description of the image. | 100 | | draggable | false | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable). If `false`, the element can not be dragged. If `true`, the element can be dragged. | 101 | | decoding | 'async' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decoding). If `async`, decode the image asynchronously to reduce delay in presenting other content. If `sync`, decode the image synchronously for atomic presentation with other content. If `default`, default mode, which indicates no preference for the decoding mode. The browser decides what is best for the user. | 102 | | width | '100%' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/width). An integer value indicating the width of the image. | 103 | | height | '100%' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/height). An integer value indicating the height of the image. | 104 | | classes | ' ' | Additional classes to be added to the `` element. | 105 | | placeholder | 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8+fOvJAAI7AMKHxaZiQAAAABJRU5ErkJggg==' | If `loading='lazy'`, this value is the placeholder until the image is loaded. Accepts strings, including paths or base64 images such as the default. | 106 | | on:load | event | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onload). An event forwarded from the `` element inside the `` element when the image is loaded. | 107 | | --transition | 'filter cubic-bezier(0.4, 0, 0.2, 1) 300ms' | The CSS transition that occurs on image load. | 108 | | --filter | 'blur(2px)' | The filter to apply to the image when unloaded. Transitions out when image is loaded. | 109 | 110 | ## License 111 | 112 | [MIT](https://github.com/sawyerclick/svelte-lazy-loader/blob/master/LICENSE) 113 | -------------------------------------------------------------------------------- /src/routes/index.svelte.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "svelte-lazy-loader" 3 | --- 4 | 5 | 11 | 12 | # svelte-lazy-loader 13 | 14 | By [Sawyer Click](https://github.com/SawyerClick) 15 | 16 | A component library for effortlessly loading media using a shared IntersectionObserver instance if native lazy-loading doesn't exist. 17 | 18 | ## Table of Contents 19 | - [svelte-lazy-loader](#svelte-lazy-loader) 20 | - [Table of Contents](#table-of-contents) 21 | - [Installation](#installation) 22 | - [Components](#components) 23 | - [Image](#image) 24 | - [Usage](#usage) 25 | - [API](#api) 26 | - [Picture](#picture) 27 | - [Usage](#usage-1) 28 | - [API](#api-1) 29 | - [License](#license) 30 | 31 | ## Installation 32 | 33 | ```bash 34 | npm install svelte-lazy-loader 35 | ``` 36 | 37 | ## Components 38 | 39 | ### Image 40 | 41 | #### Usage 42 | 43 | An expansion of the HTMLImageElement that, if a browser cannot natively lazy-load, uses a shared IntersectionObserver instance to performantly lazy-load images. This components takes several native attributes and passes them through to the underlying HTMLImageElement. A few component-specific props are available to facilitate lazy-loading. 44 | 45 | The out-of-the-box implementation of this component features a blur transition effect. The CSS can be altered using [Svelte's style props](https://svelte.dev/docs#template-syntax-component-directives---style-props). 46 | 47 | 48 | ```svelte 49 | 52 | 53 | A few tourists walk up the lawn to the side of the old stone San Felipe del Morro Castle in San Juan, Puerto Rico 54 | ``` 55 | 56 | A few tourists walk up the lawn to the side of the old stone San Felipe del Morro Castle in San Juan, Puerto Rico 57 | 58 | #### API 59 | 60 | | Parameter | Default | Description | 61 | | -------- | ------- | ----------- | 62 | | src | `placeholder` | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/src). The path to the image. Defaults to the `{placeholder}` prop. | 63 | | srcset | `src` | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/srcset). If passed, defines which images should be presented. Defaults to the `src` prop, which defaults to the `placeholder` prop. | 64 | | loading | 'lazy' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/loading). If `lazy`, lazy-load image through native browser functionality if it exists or through IntersectionObserver if it doesn't. If `eager`, image is loaded immediately. | 65 | | alt | ' ' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/alt). A description of the image. | 66 | | draggable | false | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable). If `false`, the element can not be dragged. If `true`, the element can be dragged. | 67 | | decoding | 'async' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decoding). If `async`, decode the image asynchronously to reduce delay in presenting other content. If `sync`, decode the image synchronously for atomic presentation with other content. If `default`, default mode, which indicates no preference for the decoding mode. The browser decides what is best for the user. | 68 | | width | '100%' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/width). An integer value indicating the width of the image. | 69 | | height | '100%' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/height). An integer value indicating the height of the image. | 70 | | classes | ' ' | Additional classes to be added to the `` element. | 71 | | placeholder | 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8+fOvJAAI7AMKHxaZiQAAAABJRU5ErkJggg==' | If `loading='lazy'`, this value is the placeholder until the image is loaded. Accepts strings, including paths or base64 images such as the default. | 72 | | on:load | event | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onload). An event forwarded from the `` element when the image is loaded. | 73 | | --transition | 'filter cubic-bezier(0.4, 0, 0.2, 1) 300ms' | The CSS transition that occurs on image load. | 74 | | --filter | 'blur(2px)' | The filter to apply to the image when unloaded. Transitions out when image is loaded. | 75 | 76 | ### Picture 77 | 78 | #### Usage 79 | 80 | Similarly to the above Image component, this component uses a shared IntersectionObserver instance to performantly lazy-load images if native lazy-loading doesn't exist. Sources can be passed through a default [slot](https://svelte.dev/tutorial/slots) while `` attributes are passed as props using the API below. `` elements' `srcset` attributed should be set at `data-srcset`. 81 | 82 | Similar to above, the out-of-the-box implementation of this component features a blur transition effect. The CSS can be altered using [Svelte's style props](https://svelte.dev/docs#template-syntax-component-directives---style-props). 83 | 84 | This example uses [vite-imagetools](https://www.npmjs.com/package/vite-imagetools) create images at compile-time via [sharp](https://sharp.pixelplumbing.com/). 85 | 86 | *Note: The `srcset` attribute for `` elements should be set at data-srcset for lazyloading to work properly.* 87 | 88 | ```svelte 89 | 95 | 96 | 97 | {#each sources as { src, format }} 98 | 99 | {/each} 100 | 101 | ``` 102 | 103 | 104 | {#each sources as { src, format }} 105 | 106 | {/each} 107 | 108 | 109 | #### API 110 | 111 | | Parameter | Default | Description | 112 | | -------- | ------- | ----------- | 113 | | src | `placeholder` | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/src). The path to the image. Defaults to the `{placeholder}` prop. | 114 | | loading | 'lazy' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/loading). If `lazy`, lazy-load image through native browser functionality if it exists or through IntersectionObserver if it doesn't. If `eager`, image is loaded immediately. | 115 | | alt | ' ' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/alt). A description of the image. | 116 | | draggable | false | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable). If `false`, the element can not be dragged. If `true`, the element can be dragged. | 117 | | decoding | 'async' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decoding). If `async`, decode the image asynchronously to reduce delay in presenting other content. If `sync`, decode the image synchronously for atomic presentation with other content. If `default`, default mode, which indicates no preference for the decoding mode. The browser decides what is best for the user. | 118 | | width | '100%' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/width). An integer value indicating the width of the image. | 119 | | height | '100%' | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/height). An integer value indicating the height of the image. | 120 | | classes | ' ' | Additional classes to be added to the `` element. | 121 | | placeholder | 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8+fOvJAAI7AMKHxaZiQAAAABJRU5ErkJggg==' | If `loading='lazy'`, this value is the placeholder until the image is loaded. Accepts strings, including paths or base64 images such as the default. | 122 | | on:load | event | [Defined as usual](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onload). An event forwarded from the `` element inside the `` element when the image is loaded. | 123 | | --transition | 'filter cubic-bezier(0.4, 0, 0.2, 1) 300ms' | The CSS transition that occurs on image load. | 124 | | --filter | 'blur(2px)' | The filter to apply to the image when unloaded. Transitions out when image is loaded. | 125 | 126 | ## License 127 | 128 | [MIT](https://github.com/sawyerclick/svelte-lazy-loader/blob/master/LICENSE) -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Fira Code'; 3 | src: url('/fonts/firacode.ttf'); 4 | font-weight: 1 999; 5 | } 6 | 7 | :root { 8 | --well: 720px; 9 | --mono: 'Fira Code', 'Fira Mono', Menlo, Consolas, 'DejaVu Sans Mono', monospace; 10 | --lightest: #f4f4f5; 11 | --lighter: #e4e4e7; 12 | --light: #d4d4d8; 13 | --dark: hsl(220, 13%, 18%); 14 | /* One Dark colours (accurate as of commit 8ae45ca on 6 Sep 2018) */ 15 | --mono-1: hsl(220, 14%, 71%); 16 | --mono-2: hsl(220, 9%, 55%); 17 | --mono-3: hsl(220, 10%, 40%); 18 | --hue-1: hsl(187, 47%, 55%); 19 | --hue-2: hsl(207, 82%, 66%); 20 | --hue-3: hsl(286, 60%, 67%); 21 | --hue-4: hsl(95, 38%, 62%); 22 | --hue-5: hsl(355, 65%, 65%); 23 | --hue-5-2: hsl(5, 48%, 51%); 24 | --hue-6: hsl(29, 54%, 61%); 25 | --hue-6-2: hsl(39, 67%, 69%); 26 | --syntax-fg: var(--mono-1); 27 | --syntax-bg: var(--dark); 28 | --syntax-gutter: hsl(220, 14%, 45%); 29 | --syntax-guide: hsla(220, 14%, 71%, 0.15); 30 | --syntax-accent: hsl(220, 100%, 66%); 31 | --syntax-selection-color: hsl(220, 13%, 28%); 32 | --syntax-gutter-background-color-selected: hsl(220, 13%, 26%); 33 | --syntax-cursor-line: hsla(220, 100%, 80%, 0.04); 34 | } 35 | 36 | body { 37 | background-color: var(--lighter); 38 | } 39 | 40 | main { 41 | max-width: var(--well); 42 | margin: 0 auto; 43 | padding: 0.5rem 1rem; 44 | } 45 | 46 | * { 47 | font-family: var(--mono); 48 | font-display: swap; 49 | } 50 | 51 | h1, 52 | h2, 53 | h3, 54 | h4, 55 | h5, 56 | h6, 57 | p { 58 | color: var(--dark); 59 | } 60 | 61 | h2 { 62 | margin-top: 3rem; 63 | } 64 | 65 | a { 66 | color: unset; 67 | text-decoration: underline; 68 | } 69 | 70 | code { 71 | color: var(--hue-6); 72 | font-weight: bold; 73 | } 74 | 75 | table { 76 | table-layout: fixed; 77 | width: 100%; 78 | } 79 | 80 | thead { 81 | border-bottom: 2px solid var(--dark); 82 | } 83 | 84 | tbody tr:nth-child(odd) { 85 | background: var(--light); 86 | } 87 | 88 | tr { 89 | text-align: left; 90 | } 91 | 92 | td { 93 | padding: 0.5rem; 94 | overflow-wrap: break-word; 95 | } 96 | 97 | /** 98 | * One Dark theme for prism.js 99 | * Based on Atom's One Dark theme: https://github.com/atom/atom/tree/master/packages/one-dark-syntax 100 | */ 101 | 102 | code[class*='language-'], 103 | pre[class*='language-'] { 104 | background: var(--syntax-bg); 105 | color: var(--mono-1); 106 | text-shadow: 0 1px rgba(0, 0, 0, 0.3); 107 | font-family: var(--mono); 108 | font-weight: normal; 109 | direction: ltr; 110 | text-align: left; 111 | white-space: pre-wrap; 112 | word-spacing: normal; 113 | word-break: normal; 114 | line-height: 1.5; 115 | -moz-tab-size: 2; 116 | -o-tab-size: 2; 117 | tab-size: 2; 118 | -webkit-hyphens: none; 119 | -moz-hyphens: none; 120 | -ms-hyphens: none; 121 | hyphens: none; 122 | } 123 | 124 | /* Selection */ 125 | code[class*='language-']::-moz-selection, 126 | code[class*='language-'] *::-moz-selection, 127 | pre[class*='language-'] *::-moz-selection { 128 | background: var(--syntax-selection-color); 129 | color: inherit; 130 | text-shadow: none; 131 | } 132 | 133 | code[class*='language-']::selection, 134 | code[class*='language-'] *::selection, 135 | pre[class*='language-'] *::selection { 136 | background: var(--syntax-selection-color); 137 | color: inherit; 138 | text-shadow: none; 139 | } 140 | 141 | /* Code blocks */ 142 | pre[class*='language-'] { 143 | padding: 1em; 144 | margin: 0.5em 0; 145 | overflow: auto; 146 | border-radius: 0.3em; 147 | } 148 | 149 | /* Inline code */ 150 | :not(pre) > code[class*='language-'] { 151 | padding: 0.2em 0.3em; 152 | border-radius: 0.3em; 153 | white-space: normal; 154 | } 155 | 156 | /* Print */ 157 | @media print { 158 | code[class*='language-'], 159 | pre[class*='language-'] { 160 | text-shadow: none; 161 | } 162 | } 163 | 164 | .token.comment, 165 | .token.prolog, 166 | .token.cdata { 167 | color: var(--mono-3); 168 | } 169 | 170 | .token.doctype, 171 | .token.punctuation, 172 | .token.entity { 173 | color: var(--mono-1); 174 | } 175 | 176 | .token.attr-name, 177 | .token.class-name, 178 | .token.boolean, 179 | .token.constant, 180 | .token.number, 181 | .token.atrule { 182 | color: var(--hue-6); 183 | } 184 | 185 | .token.keyword { 186 | color: var(--hue-3); 187 | } 188 | 189 | .token.property, 190 | .token.tag, 191 | .token.symbol, 192 | .token.deleted, 193 | .token.important { 194 | color: var(--hue-5); 195 | } 196 | 197 | .token.selector, 198 | .token.string, 199 | .token.char, 200 | .token.builtin, 201 | .token.inserted, 202 | .token.regex, 203 | .token.attr-value, 204 | .token.attr-value > .token.punctuation { 205 | color: var(--hue-4); 206 | } 207 | 208 | .token.variable, 209 | .token.operator, 210 | .token.function { 211 | color: var(--hue-2); 212 | } 213 | 214 | .token.url { 215 | color: var(--hue-1); 216 | } 217 | 218 | /* HTML overrides */ 219 | .token.attr-value > .token.punctuation.attr-equals, 220 | .token.special-attr > .token.attr-value > .token.value.css { 221 | color: var(--mono-1); 222 | } 223 | 224 | /* CSS overrides */ 225 | .language-css .token.selector { 226 | color: var(--hue-5); 227 | } 228 | 229 | .language-css .token.property { 230 | color: var(--mono-1); 231 | } 232 | 233 | .language-css .token.function, 234 | .language-css .token.url > .token.function { 235 | color: var(--hue-1); 236 | } 237 | 238 | .language-css .token.url > .token.string.url { 239 | color: var(--hue-4); 240 | } 241 | 242 | .language-css .token.important, 243 | .language-css .token.atrule .token.rule { 244 | color: var(--hue-3); 245 | } 246 | 247 | /* JS overrides */ 248 | .language-javascript .token.operator { 249 | color: var(--hue-3); 250 | } 251 | 252 | .language-javascript 253 | .token.template-string 254 | > .token.interpolation 255 | > .token.interpolation-punctuation.punctuation { 256 | color: var(--hue-5-2); 257 | } 258 | 259 | /* JSON overrides */ 260 | .language-json .token.operator { 261 | color: var(--mono-1); 262 | } 263 | 264 | .language-json .token.null.keyword { 265 | color: var(--hue-6); 266 | } 267 | 268 | /* MD overrides */ 269 | .language-markdown .token.url, 270 | .language-markdown .token.url > .token.operator, 271 | .language-markdown .token.url-reference.url > .token.string { 272 | color: var(--mono-1); 273 | } 274 | 275 | .language-markdown .token.url > .token.content { 276 | color: var(--hue-2); 277 | } 278 | 279 | .language-markdown .token.url > .token.url, 280 | .language-markdown .token.url-reference.url { 281 | color: var(--hue-1); 282 | } 283 | 284 | .language-markdown .token.blockquote.punctuation, 285 | .language-markdown .token.hr.punctuation { 286 | color: var(--mono-3); 287 | font-style: italic; 288 | } 289 | 290 | .language-markdown .token.code-snippet { 291 | color: var(--hue-4); 292 | } 293 | 294 | .language-markdown .token.bold .token.content { 295 | color: var(--hue-6); 296 | } 297 | 298 | .language-markdown .token.italic .token.content { 299 | color: var(--hue-3); 300 | } 301 | 302 | .language-markdown .token.strike .token.content, 303 | .language-markdown .token.strike .token.punctuation, 304 | .language-markdown .token.list.punctuation, 305 | .language-markdown .token.title.important > .token.punctuation { 306 | color: var(--hue-5); 307 | } 308 | 309 | /* General */ 310 | .token.bold { 311 | font-weight: bold; 312 | } 313 | 314 | .token.comment, 315 | .token.italic { 316 | font-style: italic; 317 | } 318 | 319 | .token.entity { 320 | cursor: help; 321 | } 322 | 323 | .token.namespace { 324 | opacity: 0.8; 325 | } 326 | 327 | /* Plugin overrides */ 328 | /* Selectors should have higher specificity than those in the plugins' default stylesheets */ 329 | 330 | /* Show Invisibles plugin overrides */ 331 | .token.token.tab:not(:empty):before, 332 | .token.token.cr:before, 333 | .token.token.lf:before, 334 | .token.token.space:before { 335 | color: var(--syntax-guide); 336 | text-shadow: none; 337 | } 338 | 339 | /* Toolbar plugin overrides */ 340 | /* Space out all buttons and move them away from the right edge of the code block */ 341 | div.code-toolbar > .toolbar.toolbar > .toolbar-item { 342 | margin-right: 0.4em; 343 | } 344 | 345 | /* Styling the buttons */ 346 | div.code-toolbar > .toolbar.toolbar > .toolbar-item > button, 347 | div.code-toolbar > .toolbar.toolbar > .toolbar-item > a, 348 | div.code-toolbar > .toolbar.toolbar > .toolbar-item > span { 349 | background: var(--syntax-gutter-background-color-selected); 350 | color: var(--mono-2); 351 | padding: 0.1em 0.4em; 352 | border-radius: 0.3em; 353 | } 354 | 355 | div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:hover, 356 | div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:focus, 357 | div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:hover, 358 | div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:focus, 359 | div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:hover, 360 | div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:focus { 361 | background: var(--syntax-selection-color); 362 | color: var(--mono-1); 363 | } 364 | 365 | /* Line Highlight plugin overrides */ 366 | /* The highlighted line itself */ 367 | .line-highlight.line-highlight { 368 | background: var(--syntax-cursor-line); 369 | } 370 | 371 | /* Default line numbers in Line Highlight plugin */ 372 | .line-highlight.line-highlight:before, 373 | .line-highlight.line-highlight[data-end]:after { 374 | background: var(--syntax-gutter-background-color-selected); 375 | color: var(--mono-1); 376 | padding: 0.1em 0.6em; 377 | border-radius: 0.3em; 378 | box-shadow: 0 2px 0 0 rgba(0, 0, 0, 0.2); /* same as Toolbar plugin default */ 379 | } 380 | 381 | /* Hovering over a linkable line number (in the gutter area) */ 382 | /* Requires Line Numbers plugin as well */ 383 | pre[id].linkable-line-numbers.linkable-line-numbers span.line-numbers-rows > span:hover:before { 384 | background-color: var(--syntax-cursor-line); 385 | } 386 | 387 | /* Line Numbers and Command Line plugins overrides */ 388 | /* Line separating gutter from coding area */ 389 | .line-numbers.line-numbers .line-numbers-rows, 390 | .command-line .command-line-prompt { 391 | border-right-color: var(--syntax-guide); 392 | } 393 | 394 | /* Stuff in the gutter */ 395 | .line-numbers .line-numbers-rows > span:before, 396 | .command-line .command-line-prompt > span:before { 397 | color: var(--syntax-gutter); 398 | } 399 | 400 | /* Match Braces plugin overrides */ 401 | /* Note: Outline colour is inherited from the braces */ 402 | .rainbow-braces .token.token.punctuation.brace-level-1, 403 | .rainbow-braces .token.token.punctuation.brace-level-5, 404 | .rainbow-braces .token.token.punctuation.brace-level-9 { 405 | color: var(--hue-5); 406 | } 407 | 408 | .rainbow-braces .token.token.punctuation.brace-level-2, 409 | .rainbow-braces .token.token.punctuation.brace-level-6, 410 | .rainbow-braces .token.token.punctuation.brace-level-10 { 411 | color: var(--hue-4); 412 | } 413 | 414 | .rainbow-braces .token.token.punctuation.brace-level-3, 415 | .rainbow-braces .token.token.punctuation.brace-level-7, 416 | .rainbow-braces .token.token.punctuation.brace-level-11 { 417 | color: var(--hue-2); 418 | } 419 | 420 | .rainbow-braces .token.token.punctuation.brace-level-4, 421 | .rainbow-braces .token.token.punctuation.brace-level-8, 422 | .rainbow-braces .token.token.punctuation.brace-level-12 { 423 | color: var(--hue-3); 424 | } 425 | 426 | /* Diff Highlight plugin overrides */ 427 | /* Taken from https://github.com/atom/github/blob/master/styles/variables.less */ 428 | pre.diff-highlight > code .token.token.deleted:not(.prefix), 429 | pre > code.diff-highlight .token.token.deleted:not(.prefix) { 430 | background-color: hsla(353, 100%, 66%, 0.15); 431 | } 432 | 433 | pre.diff-highlight > code .token.token.deleted:not(.prefix)::-moz-selection, 434 | pre.diff-highlight > code .token.token.deleted:not(.prefix) *::-moz-selection, 435 | pre > code.diff-highlight .token.token.deleted:not(.prefix)::-moz-selection, 436 | pre > code.diff-highlight .token.token.deleted:not(.prefix) *::-moz-selection { 437 | background-color: hsla(353, 95%, 66%, 0.25); 438 | } 439 | 440 | pre.diff-highlight > code .token.token.deleted:not(.prefix)::selection, 441 | pre.diff-highlight > code .token.token.deleted:not(.prefix) *::selection, 442 | pre > code.diff-highlight .token.token.deleted:not(.prefix)::selection, 443 | pre > code.diff-highlight .token.token.deleted:not(.prefix) *::selection { 444 | background-color: hsla(353, 95%, 66%, 0.25); 445 | } 446 | 447 | pre.diff-highlight > code .token.token.inserted:not(.prefix), 448 | pre > code.diff-highlight .token.token.inserted:not(.prefix) { 449 | background-color: hsla(137, 100%, 55%, 0.15); 450 | } 451 | 452 | pre.diff-highlight > code .token.token.inserted:not(.prefix)::-moz-selection, 453 | pre.diff-highlight > code .token.token.inserted:not(.prefix) *::-moz-selection, 454 | pre > code.diff-highlight .token.token.inserted:not(.prefix)::-moz-selection, 455 | pre > code.diff-highlight .token.token.inserted:not(.prefix) *::-moz-selection { 456 | background-color: hsla(135, 73%, 55%, 0.25); 457 | } 458 | 459 | pre.diff-highlight > code .token.token.inserted:not(.prefix)::selection, 460 | pre.diff-highlight > code .token.token.inserted:not(.prefix) *::selection, 461 | pre > code.diff-highlight .token.token.inserted:not(.prefix)::selection, 462 | pre > code.diff-highlight .token.token.inserted:not(.prefix) *::selection { 463 | background-color: hsla(135, 73%, 55%, 0.25); 464 | } 465 | 466 | /* Previewers plugin overrides */ 467 | /* Based on https://github.com/atom-community/atom-ide-datatip/blob/master/styles/atom-ide-datatips.less and https://github.com/atom/atom/blob/master/packages/one-dark-ui */ 468 | /* Border around popup */ 469 | .prism-previewer.prism-previewer:before, 470 | .prism-previewer-gradient.prism-previewer-gradient div { 471 | border-color: hsl(224, 13%, 17%); 472 | } 473 | 474 | /* Angle and time should remain as circles and are hence not included */ 475 | .prism-previewer-color.prism-previewer-color:before, 476 | .prism-previewer-gradient.prism-previewer-gradient div, 477 | .prism-previewer-easing.prism-previewer-easing:before { 478 | border-radius: 0.3em; 479 | } 480 | 481 | /* Triangles pointing to the code */ 482 | .prism-previewer.prism-previewer:after { 483 | border-top-color: hsl(224, 13%, 17%); 484 | } 485 | 486 | .prism-previewer-flipped.prism-previewer-flipped.after { 487 | border-bottom-color: hsl(224, 13%, 17%); 488 | } 489 | 490 | /* Background colour within the popup */ 491 | .prism-previewer-angle.prism-previewer-angle:before, 492 | .prism-previewer-time.prism-previewer-time:before, 493 | .prism-previewer-easing.prism-previewer-easing { 494 | background: hsl(219, 13%, 22%); 495 | } 496 | 497 | /* For angle, this is the positive area (eg. 90deg will display one quadrant in this colour) */ 498 | /* For time, this is the alternate colour */ 499 | .prism-previewer-angle.prism-previewer-angle circle, 500 | .prism-previewer-time.prism-previewer-time circle { 501 | stroke: var(--mono-1); 502 | stroke-opacity: 1; 503 | } 504 | 505 | /* Stroke colours of the handle, direction point, and vector itself */ 506 | .prism-previewer-easing.prism-previewer-easing circle, 507 | .prism-previewer-easing.prism-previewer-easing path, 508 | .prism-previewer-easing.prism-previewer-easing line { 509 | stroke: var(--mono-1); 510 | } 511 | 512 | /* Fill colour of the handle */ 513 | .prism-previewer-easing.prism-previewer-easing circle { 514 | fill: transparent; 515 | } 516 | --------------------------------------------------------------------------------