Read more (https://dev.to/ziizium/css-naming-conventions-5gd6)
At the very core of SMACSS is categorization. By categorizing CSS rules, we begin to see patterns and can define better practices around each of these patterns.
There are five types of categories:
Prepare separated set od styles for structure (layout) and components (buttons, form inputs, etc...)
.btn {
display: inline-block;
text-align: center;
border-radius: 3px;
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
color: #000;
padding: 5px 10px;
}
.btn-primary {
background: #47c4ff;
}
.btn-cancel {
background: #b5b5b5;
color: #fff;
}
<button class="btn btn-primary">Submit</button>
<button class="btn btn-cancel">Cancel</button>
.meta h1 {
font-size: 1em;
}
.post h1 {
font-size: 1.2em;
}
VS
h1 {
font-size: 1em;
}
h1.post-header {
font-size: 1.2em;
}
See Bootstrap source code: https://github.com/twbs/bootstrap/blob/main/scss/_buttons.scss
Sometimes I run into situations where I need additional CSS class names to manipulate the styles. When that happens, I categorize those situations into typical types and I give every type of them a prefix class-name, which represents that type.
<div class="common-message "> <!-- Gray Message --></div>
<div class="common-message is-approved"><!-- Green Message --></div>
<div class="common-message is-error "> <!-- Red Message --></div>
<div class="common-message is-alert "> <!--Yellow Message --></div>
.common-message{
/*component styles*/
&.is-approved { color:green; }
&.is-error { color:red; }
&.is-alert { color:yellow; }
}
In CSS
HTML vs CSS
You don't need to write such long CSS classes by hand. Use SASS mixins for that (https://gist.github.com/Bigismall/8472451f82d77dd380447bea555be163)
Do not apply HTML structures into BEM structure!
Please use SCSS
See presentation: (https://docs.google.com/presentation/d/1XffJxDlG5XSxT8MnXfQKUuf0xQ-MAxF9Bclvy6Fa3R8/edit?usp=sharing)
/* Declaration */
:root {
--main-bg-color: brown;
}
/*Usage */
button {
background-color: var(--main-bg-color);
}
//get inline defined styles
element.style.getPropertyValue("--schibsted-brand-background");
//get computed styles
getComputedStyle(element).getPropertyValue("--schibsted-brand-color");
// set variable on inline style
element.style.setProperty("--schibsted-brand-color","#00508E");
content-visibility
https://developer.mozilla.org/en-US/docs/Web/CSS/content-visibilityaspect-ratio
https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratioJavaScript basics: (https://github.com/Bigismall/js-basic)
We know that we can simulate asynchronicity, we would like to do it immediately, ignoring any delay. So we set 0ms
, and we hope the passed function will run immediately
.
However, even setting it to 0ms
does not change the way JavaScript works.
It is not that JS will abandon all other tasks and take care of the one.
Queuing rules apply. It will also not start our task in a separate thread, because it cannot.
(function () {
console.log("START");
setTimeout(function callBackOne() {
console.log("CALLBACK 1");
});
console.log("MESSAGE");
setTimeout(function callBackTwo() {
console.log("CALLBACK 2");
},0);
console.log("END");
})();
START
MESSAGE
END
CALLBACK 1
CALLBACK 2
As you can see callBackOne()
and callBackTwo()
were called with 0ms
param (or without any parameter), and their execution was moved to the end of the
queue.
So setTimeOut(function,0)
says - execute this function as soon as it is possible.
console.log([1, 5, 20, 10].sort()) //[ 1, 10, 20, 5 ]
var a = 0.1,
b = 0.2,
c = 0.3;
console.log((a + b) === c); //false
var a = 0 * 1,
b = 0 * -1;
console.log(a, b); //0 -0
console.log(a === b); //true
console.log(1 / a === 1 / b); //false
var a = 1,
b = "2",
c = a + b,
d = b + a,
e = (b + b) * 1,
f = a + a;
console.log(c, typeof c); // 12 string
console.log(d, typeof d); // 21 string
console.log(e, typeof e); // 22 number
console.log(f, typeof f); // 2 number
typeof {} === "object" //true
typeof "" === "string" //true
typeof [] === "array"; //false
console.log(Math.max()); // -Infinity
console.log(Math.min()); // Infinity
var numbers = [38, 43, 33, 43, 27, 20, 33, 17, 49, 11, 30, 27, 35, 42, 14, 32, 44, 44, 16, 44];
numbers.forEach(function (number) {
(function (number) {
setTimeout(function () {
console.log(number)
}, number);
}(number));
});
11
14
16
17
20
27
27
30
32
33
33
35
38
42
43
43
44
44
44
49
Define the even
method. Called on given array it should return just even numbers
[1, 2, 3, 4, 5, 6, 7, 8, 9].even() // 2,4,6,8
if (!Array.prototype.even) {
// won't work because of arrow function
//Array.prototype.even = () => this.filter((e) => e % 2==0);
Array.prototype.even = function () {
return this.filter((e) => e % 2 == 0);
};
}
console.log([1, 2, 3, 4, 5, 6, 7, 8, 9, 0].even());
document.querySelector("#magic").addEventListener("click", () => {
const paragraphs = [...document.querySelectorAll("#article p")];
const text = paragraphs.map((paragraph) => paragraph.innerText);
const speechUtterance = new SpeechSynthesisUtterance();
speechUtterance.lang = "pl";
speechSynthesis.cancel();
function speak (paragraphs) {
const paragraph = paragraphs.shift();
if (paragraph) {
speechUtterance.text = paragraph;
speechSynthesis.speak(speechUtterance);
speechUtterance.onend = () => speak(paragraphs);
} else {
speechUtterance.onend = null;
}
}
speak(text);
});
<script type="module">
import detectFace from "./js/detect.mjs";
import drawFace from "./js/canvas.mjs";
import drawSticker from "./js/stickers.mjs";
import initControls, {
getCurrentSticker,
displayLandmarks
} from "./js/controls.mjs";
</script>
const options = {
fastMode : true,
maxDetectedFaces: 10,
};
async function detectFace (image) {
const bitmap = await createImageBitmap(image);
const detector = new window.FaceDetector(options);
const detection = await detector.detect(bitmap);
return {bitmap, detection};
}
export default detectFace;
Do you want to be up to date? https://frontendfront.com/
Docs
Playground
import React from 'react'
type ToggleType = readonly [boolean, () => void, () => void, () => void]
/**
* @method useBoolean
*
* @param initialValue - default set to false
* @returns [value, setTrue, setFalse, toggle] methods
*
*/
export const useBoolean = (initialValue: boolean | undefined = false): ToggleType => {
const [value, setValue] = React.useState<boolean>(initialValue === true)
const setTrue = React.useCallback(() => setValue(true), [])
const setFalse = React.useCallback(() => setValue(false), [])
const toggle = React.useCallback(() => setValue((value) => !value), [])
return [value, setTrue, setFalse, toggle]
}
The Third Age of JavaScript: https://www.swyx.io/js-third-age/
In summary: Third Age JS tools will be:
The result of all of this work is both a better developer experience (faster builds, industry standard tooling) and user experience (smaller bundles, faster
feature delivery). It is the final metamorphosis of JavaScript from site scripting toy language to full application platform.
Interesting projects to track
ESBuild
https://esbuild.github.io/
Parcel
https://parceljs.org/
Rome ( one tool for everything )
https://rome.tools/
Estrella
https://github.com/rsms/estrella
function NumberList (props) {
const numbers = props.numbers;
const listItems = numbers.map((number) => <li>{number}</li>);
return (
<ul>{listItems}</ul>
);
}
<ul id="example-1">
<li v-for="item in items"
:key="item.message">
{{ item.message }}
</li>
</ul>
function Greeting (props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting/>;
}
return <GuestGreeting/>;
}
<h1 v-if="awesome">Vue is awesome!</h1>
<>
<h1>Cześć!</h1>{unreadMessages.length > 0 && <h2> Masz {unreadMessages.length} nieprzeczytanych wiadomości. </h2>}
</>
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes"
[class.selected]="hero === selectedHero"
(click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
<div *ngIf="selectedHero">
<h2>{{selectedHero.name | uppercase}} Details</h2>
<div><span>id: </span>{{selectedHero.id}}</div>
<div>
<label for="hero-name">Hero name:</label>
<input id="hero-name"
[(ngModel)]="selectedHero.name"
placeholder="name">
</div>
</div>
Basic hooks
Advanced hooks
const [state, setState] = useState(initialState);
setState(newState);
setState((prevState) => newState); //much better
The initialState argument is the state used during the initial render. In subsequent renders, it is disregarded.
If the initial state is the result of an expensive computation, you may provide a function instead, which will be executed only on the initial render:
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
Same story as timeout zero pattern
const App = () => {
console.log("Message BEFORE efect");
React.useEffect(() => {
console.log("Message IN efect");
return () => {
console.log("Message IN RETURN efect");
};
}, []);
console.log("Message AFTER effect");
return <div>Hello React..!</div>;
};
ReactDOM.render(<App />, document.getElementById("app"));
"Message BEFORE efect"
"Message AFTER effect"
"Message IN efect"
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() { //dark, because of value passed to provider
const theme = React.useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}
ReactDOM.render(<App />, document.getElementById("app"));
<BugsnagErrorBoundary>
<AssetsLoader>
<SessionProvider>
<FeatureFlagsProvider config={featureFlagsAppConfig}>
<FnPortal.PortalHost>
<NavigationContainer theme={nativeTheme.navigation} onReady={() => console.log('NAVIGATION READY')}>
<AppNavigationStack />
</NavigationContainer>
</FnPortal.PortalHost>
</FeatureFlagsProvider>
</SessionProvider>
</AssetsLoader>
</BugsnagErrorBoundary>
import React from 'react'
import UserContextType from '@mittanbud-business/shared/src/utils/query/CurrentUser/CurrentUserQuery.interface'
const UserContext = React.createContext<Partial<UserContextType>>({})
export const UserProvider = UserContext.Provider
export default UserContext
<SessionContext.Provider value={{ token, logout: logoutAction }}>
<ApolloProvider client={client}>
<UserProvider value={data?.me ?? null}>{children}</UserProvider>
</ApolloProvider>
</SessionContext.Provider>
Please read about:
export const MessageTemplateReducerInit = (): MessageTemplatesState => ({
list: true,
listTypeEdit: false,
form: false,
})
const [templatesState, templatesDispatch] = React.useReducer(MessageTemplatesReducer, {}, MessageTemplateReducerInit)
export const MessageTemplatesReducer = (state: MessageTemplatesState, action: MessageTemplatesAction): MessageTemplatesState => {
switch (action.type) {
case MessageTemplatesActionType.RESET:
return MessageTemplateReducerInit()
case MessageTemplatesActionType.MESSAGES_LIST:
return {
list: true,
listTypeEdit: false,
form: false,
}
case MessageTemplatesActionType.MESSAGES_LIST_EDIT:
return {
list: true,
listTypeEdit: true,
form: false,
}
case MessageTemplatesActionType.MESSAGE_ADD:
return {
list: false,
listTypeEdit: false,
form: true,
answerTemplate: undefined,
}
case MessageTemplatesActionType.MESSAGE_EDIT:
return {
list: false,
listTypeEdit: true,
form: true,
answerTemplate: action.payload.answerTemplate,
}
case MessageTemplatesActionType.MESSAGE_DELETE:
return {
list: true,
listTypeEdit: true,
form: false,
}
}
}
useLayoutEffect
- before commit phaseuseLayoutEffect
- after commit phaseThe signature is identical to useEffect
, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.
Prefer the standard useEffect when possible to avoid blocking visual updates.
#React useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const showInsufficientFundsAlert = React.useMemo(
() => data && !data.job.isAnswered && !data.job.isDeleted && data.job.answerAccess.code === GQLAnswerAccessCode.DENIED_INSUFFICIENT_FUNDS,
[data]
)
const showCompanyLacksSubscriptionAlert = React.useMemo(
() => data && !data.job.isAnswered && !data.job.isDeleted && data.job.answerAccess.code === GQLAnswerAccessCode.DENIED_COMPANY_LACKS_SUBSCRIPTION,
[data]
)
const allowComposeMessage = React.useMemo(() => data && !data.job.isAnswered && !data.job.isDeleted && data.job.answerAccess.isOpen && !answerJobData, [
data,
answerJobData,
])
#React useCallback
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
// initialize the api instance
const initState = React.useCallback(async () => {
const flagsClientInstance = new FeatureFlagsClient({ ...defaultConfig, ...config })
const initFF = await flagsClientInstance.init()
if (initFF) {
await setStore(flagsClientInstance.config.userId, flagsClientInstance.getFlags())
} else {
const localStorageFlags = await getStore(flagsClientInstance.config.userId)
dev.log('LOADING FEATURE FLAGS from Local storage')
flagsClientInstance.setFlags(Object.entries(localStorageFlags?.data ?? []).map(([, v]): FeatureFlagObject => v as FeatureFlagObject))
}
setFlagsClient(flagsClientInstance)
}, [defaultConfig, config])
// call the init on load
React.useEffect(() => {
initState()
}, [initState])
https://github.com/rehooks/awesome-react-hooks
function useDocumentTitle(title, retainOnUnmount = false) {
const defaultTitle = useRef(document.title);
useEffect(() => {
document.title = title;
}, [title]);
useEffect(() => {
return () => {
if (!retainOnUnmount) {
document.title = defaultTitle.current;
}
};
}, []);
}