feat(dev): add css helper function (#42148)
* feat(dev): add css helper function * add media declaration methods * use a selector to grab styles * add CSSHelp class with annoying TS warnings (no-verify) * replace indexing with getPropertyValue Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com> * connect CSSHelp to helpers, clean up types * fix: return extended object, not modify prototype * fix: unnecessary assertion error * add tests with fixtures * add afterAll document clean-up * 'fix' tests * undo jest transform config * remove erroneous comments and logs * reafactor: use existing types and narrow types * refactor test descriptions * rename files to appease Mrugesh 😄 * remove probably unnecessary document clean Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com> Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
@ -37,6 +37,313 @@ a {
|
|||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
|
export const cssString = `:root {
|
||||||
|
--building-color1: #aa80ff;
|
||||||
|
--building-color2: #66cc99;
|
||||||
|
--building-color3: #cc6699;
|
||||||
|
--building-color4: #538cc6;
|
||||||
|
--window-color1: #bb99ff;
|
||||||
|
--window-color2: #8cd9b3;
|
||||||
|
--window-color3: #d98cb3;
|
||||||
|
--window-color4: #8cb3d9;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-buildings,
|
||||||
|
.foreground-buildings {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.building-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-wrap {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sky {
|
||||||
|
background: radial-gradient(
|
||||||
|
closest-corner circle at 15% 15%,
|
||||||
|
#ffcf33,
|
||||||
|
#ffcf33 20%,
|
||||||
|
#ffff66 21%,
|
||||||
|
#bbeeff 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BACKGROUND BUILDINGS - "bb" stands for "background building" */
|
||||||
|
.bb1 {
|
||||||
|
width: 10%;
|
||||||
|
height: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb1a {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb1b {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb1c {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb1d {
|
||||||
|
width: 100%;
|
||||||
|
height: 70%;
|
||||||
|
background: linear-gradient(var(--building-color1) 50%, var(--window-color1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb1-window {
|
||||||
|
height: 10%;
|
||||||
|
background: linear-gradient(var(--building-color1), var(--window-color1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb2 {
|
||||||
|
width: 10%;
|
||||||
|
height: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb2a {
|
||||||
|
border-bottom: 5vh solid var(--building-color2);
|
||||||
|
border-left: 5vw solid transparent;
|
||||||
|
border-right: 5vw solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb2b {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
var(--building-color2),
|
||||||
|
var(--building-color2) 6%,
|
||||||
|
var(--window-color2) 6%,
|
||||||
|
var(--window-color2) 9%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb3 {
|
||||||
|
width: 10%;
|
||||||
|
height: 55%;
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
90deg,
|
||||||
|
var(--building-color3),
|
||||||
|
var(--building-color3),
|
||||||
|
var(--window-color3) 15%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb4 {
|
||||||
|
width: 11%;
|
||||||
|
height: 58%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb4a {
|
||||||
|
width: 3%;
|
||||||
|
height: 10%;
|
||||||
|
background-color: var(--building-color4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb4b {
|
||||||
|
width: 80%;
|
||||||
|
height: 5%;
|
||||||
|
background-color: var(--building-color4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb4c {
|
||||||
|
width: 100%;
|
||||||
|
height: 85%;
|
||||||
|
background-color: var(--building-color4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb4-window {
|
||||||
|
width: 18%;
|
||||||
|
height: 90%;
|
||||||
|
background-color: var(--window-color4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FOREGROUND BUILDINGS - "fb" stands for "foreground building" */
|
||||||
|
.fb1 {
|
||||||
|
width: 10%;
|
||||||
|
height: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb1a {
|
||||||
|
border-bottom: 7vh solid var(--building-color4);
|
||||||
|
border-left: 2vw solid transparent;
|
||||||
|
border-right: 2vw solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb1b {
|
||||||
|
width: 60%;
|
||||||
|
height: 10%;
|
||||||
|
background-color: var(--building-color4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb1c {
|
||||||
|
width: 100%;
|
||||||
|
height: 80%;
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
90deg,
|
||||||
|
var(--building-color4),
|
||||||
|
var(--building-color4) 10%,
|
||||||
|
transparent 10%,
|
||||||
|
transparent 15%
|
||||||
|
),
|
||||||
|
repeating-linear-gradient(
|
||||||
|
var(--building-color4),
|
||||||
|
var(--building-color4) 10%,
|
||||||
|
var(--window-color4) 10%,
|
||||||
|
var(--window-color4) 90%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb2 {
|
||||||
|
width: 10%;
|
||||||
|
height: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb2a {
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: 10vh solid var(--building-color3);
|
||||||
|
border-left: 1vw solid transparent;
|
||||||
|
border-right: 1vw solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb2b {
|
||||||
|
width: 100%;
|
||||||
|
height: 75%;
|
||||||
|
background-color: var(--building-color3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb2-window {
|
||||||
|
width: 22%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--window-color3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb3 {
|
||||||
|
width: 10%;
|
||||||
|
height: 35%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb3a {
|
||||||
|
width: 80%;
|
||||||
|
height: 15%;
|
||||||
|
background-color: var(--building-color1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb3b {
|
||||||
|
width: 100%;
|
||||||
|
height: 35%;
|
||||||
|
background-color: var(--building-color1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb3-window {
|
||||||
|
width: 25%;
|
||||||
|
height: 80%;
|
||||||
|
background-color: var(--window-color1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb4 {
|
||||||
|
width: 8%;
|
||||||
|
height: 45%;
|
||||||
|
position: relative;
|
||||||
|
left: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb4a {
|
||||||
|
border-top: 5vh solid transparent;
|
||||||
|
border-left: 8vw solid var(--building-color1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb4b {
|
||||||
|
width: 100%;
|
||||||
|
height: 89%;
|
||||||
|
background-color: var(--building-color1);
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb4-window {
|
||||||
|
width: 30%;
|
||||||
|
height: 10%;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--window-color1);
|
||||||
|
margin: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb5 {
|
||||||
|
width: 10%;
|
||||||
|
height: 33%;
|
||||||
|
position: relative;
|
||||||
|
right: 10%;
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
var(--building-color2),
|
||||||
|
var(--building-color2) 5%,
|
||||||
|
transparent 5%,
|
||||||
|
transparent 10%
|
||||||
|
),
|
||||||
|
repeating-linear-gradient(
|
||||||
|
90deg,
|
||||||
|
var(--building-color2),
|
||||||
|
var(--building-color2) 12%,
|
||||||
|
var(--window-color2) 12%,
|
||||||
|
var(--window-color2) 44%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb6 {
|
||||||
|
width: 9%;
|
||||||
|
height: 38%;
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
90deg,
|
||||||
|
var(--building-color3),
|
||||||
|
var(--building-color3) 10%,
|
||||||
|
transparent 10%,
|
||||||
|
transparent 30%
|
||||||
|
),
|
||||||
|
repeating-linear-gradient(
|
||||||
|
var(--building-color3),
|
||||||
|
var(--building-color3) 10%,
|
||||||
|
var(--window-color3) 10%,
|
||||||
|
var(--window-color3) 30%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
.sky {
|
||||||
|
background: radial-gradient(
|
||||||
|
closest-corner circle at 15% 15%,
|
||||||
|
#ffcf33,
|
||||||
|
#ffcf33 20%,
|
||||||
|
#ffff66 21%,
|
||||||
|
#bbeeff 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const testValues = {
|
const testValues = {
|
||||||
cssFullExample,
|
cssFullExample,
|
||||||
cssCodeWithCommentsRemoved
|
cssCodeWithCommentsRemoved
|
||||||
|
95
client/src/utils/css-help.test.ts
Normal file
95
client/src/utils/css-help.test.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/* global describe it expect */
|
||||||
|
import { cssString } from './__fixtures/curriculum-helpers-css';
|
||||||
|
import CSSHelp from './css-help';
|
||||||
|
|
||||||
|
describe('css-help', () => {
|
||||||
|
const doc = document;
|
||||||
|
let t: CSSHelp;
|
||||||
|
beforeEach(() => {
|
||||||
|
const style = doc.createElement('style');
|
||||||
|
style.innerHTML = cssString as string;
|
||||||
|
doc.head.appendChild(style);
|
||||||
|
t = new CSSHelp(doc);
|
||||||
|
// JSDOM does not implement latest CSSOM spec. As such,
|
||||||
|
// conditionText property needs to be manually added.
|
||||||
|
// REF: https://github.com/freeCodeCamp/freeCodeCamp/pull/42148#issuecomment-847291137
|
||||||
|
const mediaRule = t.getCSSRules('media')?.[0] as CSSMediaRule;
|
||||||
|
const conditionText = mediaRule.media[0];
|
||||||
|
mediaRule.conditionText = conditionText;
|
||||||
|
});
|
||||||
|
describe('getStyleDeclaration', () => {
|
||||||
|
it('should return a CSSStyleDeclartion array of length 1', () => {
|
||||||
|
expect(t.getStyleDeclarations('*')?.length).toEqual(1);
|
||||||
|
});
|
||||||
|
it('should return a non-empty CSSStyleDeclaration array', () => {
|
||||||
|
expect(t.getStyleDeclaration('.bb1')).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('isPropertyUsed', () => {
|
||||||
|
it('should return true on existing properties', () => {
|
||||||
|
expect(t.isPropertyUsed('height')).toBeTruthy();
|
||||||
|
});
|
||||||
|
it('should return true on existing custom properties', () => {
|
||||||
|
expect(t.isPropertyUsed('--building-color1')).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('isDeclaredAfter', () => {
|
||||||
|
it('should return true if existing style is declared after another', () => {
|
||||||
|
expect(t.getStyleRule('.bb1a')?.isDeclaredAfter('.bb1')).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('getPropertyValue', () => {
|
||||||
|
it('should return custom property value needing trim', () => {
|
||||||
|
expect(
|
||||||
|
t
|
||||||
|
.getStyleDeclaration(':root')
|
||||||
|
?.getPropertyValue('--building-color1')
|
||||||
|
.trim()
|
||||||
|
).toEqual('#aa80ff');
|
||||||
|
});
|
||||||
|
it('should return value to existing property', () => {
|
||||||
|
expect(
|
||||||
|
t.getStyleDeclaration('.bb4a')?.getPropertyValue('background-color')
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
|
it('should return property value without evaluating result', () => {
|
||||||
|
expect(
|
||||||
|
t.getStyleDeclaration('.bb4a')?.getPropertyValue('background-color')
|
||||||
|
).toEqual('var(--building-color4)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('getCSSRules', () => {
|
||||||
|
it('should return a CSSRules array of length 1', () => {
|
||||||
|
expect(t.getCSSRules('media')?.length).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('getRuleListsWithinMedia', () => {
|
||||||
|
it('should return a CSSMediaRule array with a selectable CSSStyleRule', () => {
|
||||||
|
expect(
|
||||||
|
t
|
||||||
|
.getRuleListsWithinMedia('(max-width: 1000px)')
|
||||||
|
.find(x => x.selectorText === '.sky')
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
|
it('should return CSSStyleDeclaration property with complex value', () => {
|
||||||
|
// NOTE: JSDOM causes value to have tabbed characters, DOM has single-line values.
|
||||||
|
expect(
|
||||||
|
t
|
||||||
|
.getRuleListsWithinMedia('(max-width: 1000px)')
|
||||||
|
.find(x => x.selectorText === '.sky')?.style?.background
|
||||||
|
).toEqual(
|
||||||
|
`radial-gradient(
|
||||||
|
closest-corner circle at 15% 15%,
|
||||||
|
#ffcf33,
|
||||||
|
#ffcf33 20%,
|
||||||
|
#ffff66 21%,
|
||||||
|
#bbeeff 100%
|
||||||
|
)`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
document.body.innerHTML = '';
|
||||||
|
document.head.innerHTML = '';
|
||||||
|
});
|
||||||
|
});
|
105
client/src/utils/css-help.ts
Normal file
105
client/src/utils/css-help.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
export interface ExtendedStyleRule extends CSSStyleRule {
|
||||||
|
isDeclaredAfter: (selector: string) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getIsDeclaredAfter = (styleRule: CSSStyleRule) => (selector: string) => {
|
||||||
|
const cssStyleRules = Array.from(
|
||||||
|
styleRule.parentStyleSheet?.cssRules || []
|
||||||
|
)?.filter(ele => ele.type === CSSRule.STYLE_RULE) as CSSStyleRule[];
|
||||||
|
const previousStyleRule = cssStyleRules.find(
|
||||||
|
ele => ele?.selectorText === selector
|
||||||
|
);
|
||||||
|
if (!previousStyleRule) return false;
|
||||||
|
const currPosition = Array.from(
|
||||||
|
styleRule.parentStyleSheet?.cssRules || []
|
||||||
|
).indexOf(styleRule);
|
||||||
|
const prevPosition = Array.from(
|
||||||
|
previousStyleRule?.parentStyleSheet?.cssRules || []
|
||||||
|
).indexOf(previousStyleRule);
|
||||||
|
return currPosition > prevPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CSSHelp {
|
||||||
|
doc: HTMLDocument;
|
||||||
|
constructor(doc: HTMLDocument) {
|
||||||
|
this.doc = doc;
|
||||||
|
}
|
||||||
|
private _getStyleRules() {
|
||||||
|
const styleSheet = this.getStyleSheet();
|
||||||
|
return this.styleSheetToCssRulesArray(styleSheet).filter(
|
||||||
|
ele => ele.type === CSSRule.STYLE_RULE
|
||||||
|
) as CSSStyleRule[];
|
||||||
|
}
|
||||||
|
|
||||||
|
getStyleDeclarations(selector: string): CSSStyleDeclaration[] {
|
||||||
|
return this._getStyleRules()
|
||||||
|
?.filter(ele => ele?.selectorText === selector)
|
||||||
|
.map(x => x.style);
|
||||||
|
}
|
||||||
|
getStyleDeclaration(selector: string): CSSStyleDeclaration | undefined {
|
||||||
|
return this._getStyleRules()?.find(ele => ele?.selectorText === selector)
|
||||||
|
?.style;
|
||||||
|
}
|
||||||
|
getStyleRule(selector: string): ExtendedStyleRule | null {
|
||||||
|
const styleRule = this._getStyleRules()?.find(
|
||||||
|
ele => ele?.selectorText === selector
|
||||||
|
);
|
||||||
|
if (styleRule) {
|
||||||
|
return {
|
||||||
|
...styleRule,
|
||||||
|
isDeclaredAfter: (selector: string) =>
|
||||||
|
getIsDeclaredAfter(styleRule)(selector)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getCSSRules(element?: string): CSSRule[] {
|
||||||
|
const styleSheet = this.getStyleSheet();
|
||||||
|
const cssRules = this.styleSheetToCssRulesArray(styleSheet);
|
||||||
|
switch (element) {
|
||||||
|
case 'media':
|
||||||
|
return cssRules.filter(ele => ele.type === CSSRule.MEDIA_RULE);
|
||||||
|
case 'fontface':
|
||||||
|
return cssRules.filter(ele => ele.type === CSSRule.FONT_FACE_RULE);
|
||||||
|
case 'import':
|
||||||
|
return cssRules.filter(ele => ele.type === CSSRule.IMPORT_RULE);
|
||||||
|
case 'keyframes':
|
||||||
|
return cssRules.filter(ele => ele.type === CSSRule.KEYFRAMES_RULE);
|
||||||
|
default:
|
||||||
|
return cssRules;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isPropertyUsed(property: string): boolean {
|
||||||
|
return this._getStyleRules().some(ele =>
|
||||||
|
ele.style?.getPropertyValue(property)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
getRuleListsWithinMedia(conditionText: string): CSSStyleRule[] {
|
||||||
|
const medias = this.getCSSRules('media') as CSSMediaRule[];
|
||||||
|
const cond = medias?.find(x => x.conditionText === conditionText);
|
||||||
|
const cssRules = cond?.cssRules;
|
||||||
|
return Array.from(cssRules || []) as CSSStyleRule[];
|
||||||
|
}
|
||||||
|
getStyleSheet(): CSSStyleSheet | null {
|
||||||
|
// TODO: Change selector to match exactly 'styles.css'
|
||||||
|
const link: HTMLLinkElement | null = this.doc?.querySelector(
|
||||||
|
"link[href*='styles']"
|
||||||
|
);
|
||||||
|
const style: HTMLStyleElement | null = this.doc?.querySelector('style');
|
||||||
|
if (link) {
|
||||||
|
return link.sheet;
|
||||||
|
} else if (style) {
|
||||||
|
return style.sheet;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
styleSheetToCssRulesArray(
|
||||||
|
styleSheet: ReturnType<CSSHelp['getStyleSheet']>
|
||||||
|
): CSSRule[] {
|
||||||
|
return Array.from(styleSheet?.cssRules || []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSHelp;
|
@ -1,5 +1,6 @@
|
|||||||
import { parse } from '@babel/parser';
|
import { parse } from '@babel/parser';
|
||||||
import generate from '@babel/generator';
|
import generate from '@babel/generator';
|
||||||
|
import CSSHelp from './css-help';
|
||||||
|
|
||||||
const removeHtmlComments = (str: string): string =>
|
const removeHtmlComments = (str: string): string =>
|
||||||
str.replace(/<!--[\s\S]*?(-->|$)/g, '');
|
str.replace(/<!--[\s\S]*?(-->|$)/g, '');
|
||||||
@ -18,6 +19,7 @@ export const removeJSComments = (codeStr: string): string => {
|
|||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const ast = parse(codeStr);
|
const ast = parse(codeStr);
|
||||||
|
// TODO: Sort out type error on ast
|
||||||
const { code } = generate(ast, options, codeStr);
|
const { code } = generate(ast, options, codeStr);
|
||||||
return code;
|
return code;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -32,7 +34,8 @@ const removeWhiteSpace = (str = ''): string => {
|
|||||||
const curriculumHelpers = {
|
const curriculumHelpers = {
|
||||||
removeHtmlComments,
|
removeHtmlComments,
|
||||||
removeCssComments,
|
removeCssComments,
|
||||||
removeWhiteSpace
|
removeWhiteSpace,
|
||||||
|
CSSHelp
|
||||||
};
|
};
|
||||||
|
|
||||||
export default curriculumHelpers;
|
export default curriculumHelpers;
|
||||||
|
@ -13,13 +13,13 @@ Media Queries are a new technique introduced in CSS3 that change the presentatio
|
|||||||
|
|
||||||
Media Queries consist of a media type, and if that media type matches the type of device the document is displayed on, the styles are applied. You can have as many selectors and styles inside your media query as you want.
|
Media Queries consist of a media type, and if that media type matches the type of device the document is displayed on, the styles are applied. You can have as many selectors and styles inside your media query as you want.
|
||||||
|
|
||||||
Here's an example of a media query that returns the content when the device's width is less than or equal to 100px:
|
Here's an example of a media query that returns the content when the device's width is less than or equal to `100px`:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
@media (max-width: 100px) { /* CSS Rules */ }
|
@media (max-width: 100px) { /* CSS Rules */ }
|
||||||
```
|
```
|
||||||
|
|
||||||
and the following media query returns the content when the device's height is more than or equal to 350px:
|
and the following media query returns the content when the device's height is more than or equal to `350px`:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
@media (min-height: 350px) { /* CSS Rules */ }
|
@media (min-height: 350px) { /* CSS Rules */ }
|
||||||
@ -33,38 +33,25 @@ Add a media query, so that the `p` tag has a `font-size` of `10px` when the devi
|
|||||||
|
|
||||||
# --hints--
|
# --hints--
|
||||||
|
|
||||||
You should declare a `@media` query for devices with a `height` less than or equal to 800px.
|
You should declare a `@media` query for devices with a `height` less than or equal to `800px`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
assert(
|
const media = new __helpers.CSSHelp(document).getCSSRules('media');
|
||||||
$('style')
|
assert(media.some(x => x.conditionText?.includes('(max-height: 800px)')));
|
||||||
.text()
|
|
||||||
.replace(/\s/g, '')
|
|
||||||
.match(/@media\(max-height:800px\)/g)
|
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Your `p` element should have a `font-size` of 10px when the device `height` is less than or equal to 800px.
|
Your `p` element should have a `font-size` of `10px` when the device `height` is less than or equal to `800px`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
assert(
|
const rules = new __helpers.CSSHelp(document).getRuleListsWithinMedia('(max-height: 800px)');
|
||||||
$('style')
|
assert(rules?.find(x => x.selectorText === 'p')?.style.fontSize === "10px");
|
||||||
.text()
|
|
||||||
.replace(/\s/g, '')
|
|
||||||
.match(/@media\(max-height:800px\){p{font-size:10px;?}}/g)
|
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Your `p` element should have an initial `font-size` of 20px when the device `height` is more than 800px.
|
Your `p` element should have an initial `font-size` of `20px` when the device `height` is more than `800px`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
assert(
|
const ifPFirst = new __helpers.CSSHelp(document).getCSSRules()?.find(x => x?.selectorText === 'p' || x?.media);
|
||||||
$('style')
|
assert(ifPFirst?.style?.fontSize === '20px');
|
||||||
.text()
|
|
||||||
.replace(/\s/g, '')
|
|
||||||
.replace(/@media.*}/g, '')
|
|
||||||
.match(/p{font-size:20px;?}/g)
|
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# --seed--
|
# --seed--
|
||||||
|
Reference in New Issue
Block a user