From 9511677a06e92b293c3818aee3a41425c8791e5b Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Sun, 31 Jul 2022 23:13:02 +0000 Subject: [PATCH 1/2] Commit new version --- .eslintignore | 1 + .eslintrc.json | 11 + .github/workflows/build_test.yml | 45 + .gitignore | 4 +- .prettierignore | 1 + .prettierrc.json | 6 + README.md | 150 +- package-lock.json | 19260 +++++++++++++++++++++++----- package.json | 53 +- src/constants.ts | 33 + src/cqfill.ts | 62 - src/engine.ts | 1396 +- src/evaluate.ts | 471 + src/globals.d.ts | 14 + src/missing-types.d.ts | 49 - src/parser.ts | 426 + src/transform.ts | 695 + src/utils/ast.ts | 43 + src/utils/css.ts | 1576 +++ src/utils/parse-media-feature.ts | 245 + src/utils/parse-media-query.ts | 197 + src/wpt.ts | 39 + tests/@supports.html | 34 + tests/and.html | 79 +- tests/cdn_link.html | 22 +- tests/cdn_link_iife.html | 22 +- tests/dynamic.html | 74 +- tests/dynamic_container.html | 29 +- tests/dynamic_link.html | 30 +- tests/dynamic_style.html | 26 +- tests/empty_href.html | 6 +- tests/index.html | 59 +- tests/layout_query.html | 66 +- tests/named.html | 125 +- tests/named_shorthand.html | 121 +- tests/nested.html | 76 +- tests/not.html | 59 +- tests/or.html | 59 +- tests/parsing_error1.html | 58 +- tests/parsing_error2.html | 68 +- tests/parsing_error3.html | 54 - tests/pseudo_element.html | 107 + tests/realworldstyles1.html | 101 +- tests/relative_urls.css | 8 +- tests/relative_urls.html | 32 +- tests/runner.html | 70 + tests/simple.html | 66 +- tests/simple_link.html | 22 +- tests/simple_new_long_syntax.html | 66 +- tests/simple_new_syntax.html | 66 +- tests/simple_style.html | 16 +- tests/simple_style_iife.html | 50 - tests/test-utils.js | 20 +- tests/wpt.ts | 487 + tsconfig.json | 14 + 55 files changed, 21998 insertions(+), 4971 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 .github/workflows/build_test.yml create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100644 src/constants.ts delete mode 100644 src/cqfill.ts create mode 100644 src/evaluate.ts create mode 100644 src/globals.d.ts delete mode 100644 src/missing-types.d.ts create mode 100644 src/parser.ts create mode 100644 src/transform.ts create mode 100644 src/utils/ast.ts create mode 100644 src/utils/css.ts create mode 100644 src/utils/parse-media-feature.ts create mode 100644 src/utils/parse-media-query.ts create mode 100644 src/wpt.ts create mode 100644 tests/@supports.html delete mode 100644 tests/parsing_error3.html create mode 100644 tests/pseudo_element.html create mode 100644 tests/runner.html delete mode 100644 tests/simple_style_iife.html create mode 100644 tests/wpt.ts create mode 100644 tsconfig.json diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..7773828 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +dist/ \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..cabf75e --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,11 @@ +{ + "root": true, + "env": { + "browser": true, + "es6": true, + "node": true + }, + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"] +} diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml new file mode 100644 index 0000000..e6be61c --- /dev/null +++ b/.github/workflows/build_test.yml @@ -0,0 +1,45 @@ +name: Build & Test + +on: + push: + branches: [main] + pull_request: + types: [opened, synchronize] + +jobs: + build: + runs-on: ubuntu-latest + env: + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + WPT_MANIFEST: ${{ github.workspace }}/wpt/MANIFEST.json + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '16' + - uses: actions/setup-python@v3 + with: + python-version: '3.x' + - uses: actions/checkout@v3 + with: + repository: devknoll/wpt + path: wpt + ref: x-polyfill-all-tests + + - name: Build + run: | + npm install + npm run build:wpt + + - name: Setup WPT + run: | + cd wpt + pip install virtualenv + ./wpt make-hosts-file | sudo tee -a /etc/hosts + - name: Run Tests + run: | + npm run serve & + ./wpt/wpt manifest + ./wpt/wpt serve --inject-script=${{ github.workspace }}/dist/src/cqfill.modern.js & + npm test diff --git a/.gitignore b/.gitignore index 7ba7abe..db4c6d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ -cqfill.js -cqfill.min.js -cqfill.iife.min.js +dist node_modules \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..7773828 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +dist/ \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..993c327 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "bracketSpacing": false, + "singleQuote": true, + "trailingComma": "es5", + "arrowParens": "avoid" +} diff --git a/README.md b/README.md index 07c98d0..6a1c455 100644 --- a/README.md +++ b/README.md @@ -1,80 +1,120 @@ # Container Query Polyfill +A small (9 kB compressed) polyfill for CSS Container Queries using [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) and [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) supporting the full [`@container`](https://drafts.csswg.org/css-contain-3/) query syntax: -A tiny polyfill for [CSS Container Queries][mdn], weighing about 1.6kB brotli’d. It transpiles CSS code on the client-side and implements Container Query functionality using [ResizeObserver] and [MutationObserver]. + * Discrete queries (`width: 300` and `min-width: 300px`) + * Range queries (`200px < width < 400px` and `width < 400px`) + * Container relative length units (`cqw`, `cqh`, `cqi`, `cqb`, `cqmin`, and `cqmax`) in properties and keyframes -## Usage - -Ideally, the polyfill is only loaded if the browser doesn’t support Container Queries natively. In a modern setup with a bundler that uses ES modules, the following snippet should work: +## Getting Started +To use the polyfill, add this script tag to the head of your document: : ```js -const supportsContainerQueries = "container" in document.documentElement.style; -if (!supportsContainerQueries) { - import("container-query-polyfill"); -} + ``` -If you are in a legacy setup (or just want to prototype quickly), there’s also an IIFE version that you can include using a ` +For the best user experience, it's recommended that you initially only use the polyfill for content below-the-fold and use `@supports` queries to temporarily replace it with a loading indicator until the polyfill is ready to display it: + +```css +@supports not (container-type: inline-size) { + .container, footer { + display: none; + } + + .loader { + display: flex; + } +} ``` -## Browser support +You can view a more complete demo [here](https://codesandbox.io/s/smoosh-glitter-m2ub4w?file=/index.html). On sufficiently fast networks and devices, or devices that natively support Container Queries, this loading indicator will never be displayed. -The polyfill relies on [ResizeObserver], [MutationObserver] and [`:is()`][is selector]. Therefore, it should work in all modern browsers, specifically Chrome/Edge 88+, Firefox 78+ and Safari 14+. +> **Note** +> Keep in mind that this technique effectively trades off LCP for less jank during initial load, so you may see regressions in the former as a result, particularly on low end devices. -## Feature support & limitations +## Limitations -My aim is to make the polyfill work correctly for the _majority_ of use-cases, but cut corners where possible to keep the polyfill simple(-ish), small and efficient. The limitations arising from these tradeoffs are listed below. +* **CSS first**: The polyfill currently only supports ` +
+

Ohai

+
+ diff --git a/tests/and.html b/tests/and.html index 5858379..fdc0c64 100644 --- a/tests/and.html +++ b/tests/and.html @@ -1,50 +1,57 @@ - +

H1

+
diff --git a/tests/index.html b/tests/index.html index 59e81bd..ec6e0a1 100644 --- a/tests/index.html +++ b/tests/index.html @@ -10,41 +10,40 @@ +

H1

diff --git a/tests/parsing_error2.html b/tests/parsing_error2.html index 26f7fd9..bc1baad 100644 --- a/tests/parsing_error2.html +++ b/tests/parsing_error2.html @@ -1,51 +1,53 @@ - +

Ohai

diff --git a/tests/pseudo_element.html b/tests/pseudo_element.html new file mode 100644 index 0000000..afee500 --- /dev/null +++ b/tests/pseudo_element.html @@ -0,0 +1,107 @@ + + +
+

Ohai

+
+ diff --git a/tests/realworldstyles1.html b/tests/realworldstyles1.html index 9b21d98..362c9aa 100644 --- a/tests/realworldstyles1.html +++ b/tests/realworldstyles1.html @@ -1,72 +1,75 @@ - +

Ohai

+ + + + + + diff --git a/tests/simple.html b/tests/simple.html index 3188376..6fe2384 100644 --- a/tests/simple.html +++ b/tests/simple.html @@ -1,51 +1,51 @@ - +

Ohai

- diff --git a/tests/test-utils.js b/tests/test-utils.js index c8c1c0e..c959892 100644 --- a/tests/test-utils.js +++ b/tests/test-utils.js @@ -12,7 +12,7 @@ */ export function doubleRaf() { - return new Promise((resolve) => { + return new Promise(resolve => { requestAnimationFrame(() => { requestAnimationFrame(() => { resolve(); @@ -22,16 +22,16 @@ export function doubleRaf() { } export function fail(msg) { - window.parent?.postMessage(msg, "*"); + window.parent?.postMessage(msg, '*'); } export function success() { - window.parent?.postMessage(true, "*"); + window.parent?.postMessage(true, '*'); } export function nextEvent(el, name) { - return new Promise((resolve) => - el.addEventListener(name, resolve, { once: true }) + return new Promise(resolve => + el.addEventListener(name, resolve, {once: true}) ); } @@ -48,16 +48,16 @@ export function assertEquals(a, b, msg) { } export function timeout(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); + return new Promise(resolve => setTimeout(resolve, ms)); } export async function testSuite(name, cb) { try { await Promise.race([ cb(), - timeout(2000).then(() => { - throw Error(`Timeout`); - }), + // timeout(2000).then(() => { + // throw Error(`Timeout`); + // }), ]); } catch (e) { console.error(e); @@ -65,5 +65,5 @@ export async function testSuite(name, cb) { return; } success(); - console.log("Test passed successfully"); + console.log('Test passed successfully'); } diff --git a/tests/wpt.ts b/tests/wpt.ts new file mode 100644 index 0000000..2d74063 --- /dev/null +++ b/tests/wpt.ts @@ -0,0 +1,487 @@ +/** + * Copyright 2022 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {eachLimit, retry} from 'async'; +import {readFile} from 'fs/promises'; +import {Agent} from 'http'; +import {Builder, By, until} from 'selenium-webdriver'; +import {Local} from 'browserstack-local'; + +type Capabilities = Record; + +const enum DataType { + FetchDescriptor, + Result, +} + +interface ResultData { + type: DataType.Result; + result: [number, number]; +} + +interface FetchDescriptorData { + type: DataType.FetchDescriptor; + capabilities: Capabilities; +} + +interface BrowserVersion { + name: string; + data: FetchDescriptorData | ResultData; +} + +interface BrowserDefinition { + name: string; + logo: string; + versions: BrowserVersion[]; +} + +const TEST_FILTERS: Array = [ + /-serialization.html$/, + /-computed.html$/, + /calc-evaluation.html$/, + /auto-scrollbars.html$/, + + /container-inheritance.html$/, + /container-for-shadow-dom.html$/, + /container-units-shadow.html$/, + /container-longhand-animation-type.html$/, + /container-name-invalidation.html$/, + /container-name-parsing.html$/, + /container-parsing.html$/, + /container-type-containment.html$/, + /container-type-layout-invalidation.html$/, + /container-type-parsing.html$/, + /container-units-basic.html$/, + /container-units-in-at-container-fallback.html$/, + /container-units-invalidation.html$/, + /container-units-media-queries.html$/, + /container-units-selection.html$/, + /container-units-small-viewport-fallback.html$/, + /container-units-svglength.html$/, + /container-units-typed-om.html$/, + /deep-nested-inline-size-containers.html$/, + /idlharness.html$/, + /iframe-in-container-invalidation.html$/, + /iframe-invalidation.html$/, + /percentage-padding-orthogonal.html$/, + /viewport-units-dynamic.html$/, + /viewport-units.html$/, +]; + +const SUBTEST_FILTERS: Array = [ + /calc\(.*\)/, + /max\(.*\)/, + /style\(.*\)/, + /#container width 399px after padding is applied. #second is removed from the rendering/, + /ex units/, + /ch units/, + /ex relative/, + /ch relative/, +]; + +const CHROME_DEFINITION: BrowserDefinition = { + name: 'Chrome', + logo: 'https://unpkg.com/@browser-logos/chrome@2.0.0/chrome.svg', + versions: Array.from({length: 0 /*103 - 79*/}) + .map((_, i) => 79 + i) + .filter(version => ![82].includes(version)) + .map(version => `${version}.0`) + .map(browserVersion => ({ + name: browserVersion, + data: { + type: DataType.FetchDescriptor, + capabilities: { + 'bstack:options': { + os: 'OS X', + osVersion: 'Monterey', + }, + browserName: 'Chrome', + browserVersion: browserVersion, + }, + }, + })), +}; + +const SAFARI_IOS_DEFINITION: BrowserDefinition = { + name: 'Safari (iOS)', + logo: 'https://unpkg.com/@browser-logos/safari-ios@1.0.15/safari-ios.svg', + versions: ( + [ + // ['13.7', '13.7'], + // ['14.0', '14'], + // ['14.1', '14'], + // ['15.0', '15'], + // ['15.2', '15'], + // ['15.4', '15'], + // ['15.5', '15'], + ['15.6', '15'], + ] as Array<[string, string]> + ).map(([browserVersion, osVersion]) => ({ + name: browserVersion, + data: { + type: DataType.FetchDescriptor, + capabilities: { + 'bstack:options': { + deviceName: 'iPhone 11', + osVersion, + }, + browserName: 'safari', + browserVersion, + }, + }, + })), +}; + +const SAFARI_MACOS_DEFINITION: BrowserDefinition = { + name: 'Safari (macOS)', + logo: 'https://unpkg.com/@browser-logos/safari-ios@1.0.15/safari-ios.svg', + versions: ( + [ + // ['13.1', 'Catalina'], + // ['14.1', 'Big Sur'], + // ['15.3', 'Monterey'], + ] as Array<[string, string]> + ).map(([browserVersion, osVersion]) => ({ + name: browserVersion, + data: { + type: DataType.FetchDescriptor, + capabilities: { + 'bstack:options': { + os: 'OS X', + osVersion, + }, + browserName: 'safari', + browserVersion, + }, + }, + })), +}; + +const EDGE_DEFINITION: BrowserDefinition = { + name: 'Edge', + logo: 'https://unpkg.com/@browser-logos/edge@2.0.5/edge.svg', + versions: Array.from({length: 0 /*102 - 80*/}) + .map((_, i) => 80 + i) + .filter(version => ![82].includes(version)) + .map(version => `${version}.0`) + .map(browserVersion => ({ + name: browserVersion, + data: { + type: DataType.FetchDescriptor, + capabilities: { + 'bstack:options': { + os: 'OS X', + osVersion: 'Monterey', + }, + browserName: 'Edge', + browserVersion, + }, + }, + })), +}; + +const FIREFOX_DEFINITION: BrowserDefinition = { + name: 'Firefox', + logo: 'https://unpkg.com/@browser-logos/firefox@3.0.9/firefox.svg', + versions: Array.from({length: 0 /*101 - 69*/}) + .map((_, i) => 69 + i) + .map(version => `${version}.0`) + .map(browserVersion => ({ + name: browserVersion, + data: { + type: DataType.FetchDescriptor, + capabilities: { + 'bstack:options': { + os: 'OS X', + osVersion: 'Monterey', + }, + browserName: 'Firefox', + browserVersion, + }, + }, + })), +}; + +const SAMSUNG_INTERNET_DEFINITION: BrowserDefinition = { + name: 'Samsung Internet', + logo: 'https://unpkg.com/@browser-logos/samsung-internet@4.0.6/samsung-internet.svg', + versions: [ + // '9.2', + // '10.1', + // '11.2', + // '12.0', + // '13.0', + // '14.0', + // '15.0', + // '16.0', + // '17.0', + ].map(browserVersion => ({ + name: browserVersion, + data: { + type: DataType.FetchDescriptor, + capabilities: { + 'bstack:options': { + osVersion: '12.0', + deviceName: 'Samsung Galaxy S22 Ultra', + }, + browserName: 'samsung', + browserVersion, + }, + }, + })), +}; + +const IE_DEFINITION: BrowserDefinition = { + name: 'Internet Explorer', + logo: 'https://unpkg.com/@browser-logos/internet-explorer_9-11@1.1.16/internet-explorer_9-11.svg', + versions: ['9', '10', '11'].map(browserVersion => ({ + name: browserVersion, + data: {type: DataType.Result, result: [0, 0]}, + })), +}; + +const BROWSERS: BrowserDefinition[] = [ + CHROME_DEFINITION, + SAFARI_IOS_DEFINITION, + SAFARI_MACOS_DEFINITION, + EDGE_DEFINITION, + FIREFOX_DEFINITION, + SAMSUNG_INTERNET_DEFINITION, + IE_DEFINITION, +]; + +interface Subtest { + name: string; + status: number; + PASS: number; + PRECONDITION_FAILED: number; +} + +interface TestResult { + 0: string; + 1: { + tests: Array; + }; +} + +type TestSuite = { + js: string[]; + iframe: Array<[string, string]>; +}; + +function createLocalServer(): Promise { + return new Promise((resolve, reject) => { + const server = new Local(); + server.start( + { + key: process.env.BROWSERSTACK_ACCESS_KEY, + }, + err => { + if (err) { + reject(err); + } else { + resolve(server); + } + } + ); + }); +} + +function stopLocalServer(server: Local): Promise { + return new Promise(resolve => { + server.stop(resolve); + }); +} + +async function getTests(manifestPath: string): Promise { + const manifestBuffer = await readFile(manifestPath); + const manifest = JSON.parse(manifestBuffer.toString()); + + const prefix = `css/css-contain/container-queries`; + const htmlTests = + manifest.items.testharness.css['css-contain']['container-queries']; + const refTests = + manifest.items.reftest.css['css-contain']['container-queries']; + + const iframe: Array<[string, string]> = []; + Object.keys(refTests).forEach((name, id) => { + const data = refTests[name][1][1][0]; + iframe.push( + [`ref${id}_test`, `http://web-platform.test:8000/${prefix}/${name}`], + [`ref${id}_match`, `http://web-platform.test:8000/${data[0]}`] + ); + }); + + return { + js: Object.keys(htmlTests) + .filter(name => !TEST_FILTERS.some(filter => filter.test(name))) + .map(name => `http://web-platform.test:8000/${prefix}/${name}`), + iframe, + }; +} + +function createWebDriver(capabilities: Record) { + try { + return new Builder() + .usingHttpAgent( + new Agent({ + keepAlive: true, + keepAliveMsecs: 30 * 1000, + }) + ) + .usingServer('http://hub-cloud.browserstack.com/wd/hub') + .withCapabilities({ + ...capabilities, + 'bstack:options': { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...(capabilities as any)['bstack:options'], + userName: process.env.BROWSERSTACK_USERNAME, + accessKey: process.env.BROWSERSTACK_ACCESS_KEY, + local: true, + debug: true, + consoleLogs: 'verbose', + networkLogs: true, + seleniumVersion: '4.1.0', + }, + }) + .build(); + } catch (e) { + console.warn( + `Failed while creating driver with capabilities: ${JSON.stringify( + capabilities + )}` + ); + throw e; + } +} + +async function runTestSuite( + name: string, + capabilities: Record, + testSuite: TestSuite +): Promise> { + const driver = createWebDriver(capabilities); + + try { + console.info(`[${name}] Connecting...`); + await driver.get('http://bs-local.com:9606/tests/runner.html'); + + console.info(`[${name}] Running tests...`); + await driver.executeScript( + `window.RUN_CQ_TESTS(${JSON.stringify(testSuite)})` + ); + + const resultsElem = await driver.wait( + until.elementLocated(By.id('__test_results__')), + 3 * 60 * 1000, + 'Timed out', + 5 * 1000 + ); + const result = JSON.parse(await resultsElem.getAttribute('innerHTML')); + console.info(`[${name}] Finished successfully`); + return result; + } catch (err) { + console.warn(`[${name}] Failed: ${err}`); + throw err; + } finally { + try { + await driver.close(); + await driver.quit(); + } catch { + // Some buggy WebDriver implementations could fail during closing, + // but we want to keep any results we already returned. + } + } +} + +async function tryOrDefault(fn: () => Promise, def: () => T): Promise { + try { + return await fn(); + } catch { + return def(); + } +} + +async function main() { + const manifestPath = process.env.WPT_MANIFEST; + if (!manifestPath) { + throw new Error('invariant: WPT_MANIFEST environment variable must be set'); + } + + const testSuite = await getTests(manifestPath); + console.info(`Using tests: ${JSON.stringify(testSuite, null, 4)}`); + + const tests: Array<() => Promise> = []; + const results: BrowserDefinition[] = BROWSERS.map(browser => ({ + ...browser, + versions: browser.versions.map(version => { + const result: BrowserVersion = { + ...version, + }; + tests.push(async () => { + const data = version.data; + if (data.type === DataType.FetchDescriptor) { + const results = await tryOrDefault( + async () => + await retry( + 5, + async () => + await runTestSuite( + `${browser.name} ${version.name}`, + data.capabilities, + testSuite + ) + ), + () => [] + ); + + let passed = 0; + let failed = 0; + + for (const test of results) { + if (Array.isArray(test) && Array.isArray(test[1].tests)) { + for (const subtest of test[1].tests) { + if (SUBTEST_FILTERS.some(filter => filter.test(subtest.name))) { + continue; + } + if (subtest.status === subtest.PASS) { + passed++; + } else if (subtest.status !== subtest.PRECONDITION_FAILED) { + failed++; + } + } + } + } + + result.data = {type: DataType.Result, result: [passed, failed]}; + } + }); + return result; + }), + })); + + const server = await createLocalServer(); + try { + await eachLimit(tests, 5, async test => await test()); + console.log(JSON.stringify(results, null, 2)); + } finally { + await stopLocalServer(server); + } +} + +try { + await main(); +} catch (e) { + console.error('Failed to complete tests:'); + console.error(e); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..841d0e0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "rootDir": ".", + "outDir": "dist", + "lib": ["esnext", "DOM", "DOM.Iterable"], + "strict": true, + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", + "types": [], + "esModuleInterop": true + }, + "include": ["src/**/*.ts", "tests/**/*.ts"] +} From 2892d839aefcfb4e29de052e841ede51a1544ac3 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Mon, 1 Aug 2022 21:23:59 +0000 Subject: [PATCH 2/2] Minor tweaks and additional test fixes --- README.md | 47 +++++++++-------- src/engine.ts | 130 +++++++++++++++++++++++++---------------------- src/evaluate.ts | 122 +++++++++++++++++++++++--------------------- src/parser.ts | 50 +++++++++++------- src/transform.ts | 12 ++--- 5 files changed, 195 insertions(+), 166 deletions(-) diff --git a/README.md b/README.md index 6a1c455..469b283 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ # Container Query Polyfill + A small (9 kB compressed) polyfill for CSS Container Queries using [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) and [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) supporting the full [`@container`](https://drafts.csswg.org/css-contain-3/) query syntax: - * Discrete queries (`width: 300` and `min-width: 300px`) - * Range queries (`200px < width < 400px` and `width < 400px`) - * Container relative length units (`cqw`, `cqh`, `cqi`, `cqb`, `cqmin`, and `cqmax`) in properties and keyframes +- Discrete queries (`width: 300` and `min-width: 300px`) +- Range queries (`200px < width < 400px` and `width < 400px`) +- Container relative length units (`cqw`, `cqh`, `cqi`, `cqb`, `cqmin`, and `cqmax`) in properties and keyframes ## Getting Started + To use the polyfill, add this script tag to the head of your document: : ```js @@ -25,7 +27,8 @@ For the best user experience, it's recommended that you initially only use the p ```css @supports not (container-type: inline-size) { - .container, footer { + .container, + footer { display: none; } @@ -42,9 +45,9 @@ You can view a more complete demo [here](https://codesandbox.io/s/smoosh-glitter ## Limitations -* **CSS first**: The polyfill currently only supports `