2023. 6. 30.CSS container queries
tl;dr
How to
- container query로 "볼" element에
container-type
으로 "containment context"를 지정한다. container-type
은 아래와 같은 옵션들이 있다.size
: 'containment context로 지정된 element의 "inline", "block" layout 정보를 다 쓰겠다.'inline-size
: '"inline" layout 정보만 쓰겠다.'normal
: 'layout 정보는 안쓰겠다.'
container-name
으로 naming도 할 수 있다. (grid-area
랑 비슷한 머시기라고 생각하자.)- 위 둘을 줄여서
container: {name} / {type}
로 쓸 수 있다. @container {name} ({condition}) { ... }
로 스타일을 정의한다.- container queries용 unit도 따로 있다.
cqw
: containment context element width의 1%cqh
: height의 1%cqi
: inline size의 1%cqb
: block size의 1%cqmin
: cqi와 cqb 중 작은 값cqmax
: 큰 값
"width랑 inline size랑 같은 거 아니에요?"
writing mode가 vertical일 경우를 생각해보시면...
Refs
비고
- 2023년 6월 현재 style queries는 아직 제대로 지원되지 않음
2023. 6. 26.Callback Ref
callback refs?
ref
는 보통 useRef
hook과 함께 쓰지만 ref
에 함수를 넘길 수도 있는데, 요런 패턴을 callback refs 라고 하고, 대부분 DOM node를 액세스 하는 경우에 쓰인다.
How?
놀랍게도 ref
엔 함수를 넘길 수 있는데, 요 경우 아래와 같이 node를 arg로 받게 된다.
const ref = (node) => {
// access the dom node
}
const ref = (node) => {
// access the dom node
}
사실 useRef
가 넘기는 ref
도 걍 아래와 같다.
<div
ref={(node) => {
ref.current = node;
}}
/>
<div
ref={(node) => {
ref.current = node;
}}
/>
But why?
기존의 ref
는 보통 effect 내부에서 마운트가 된 이후에 액세스하게 된다. 근데 해당 dom node에 조건부 렌더가 걸려있다면 동작하지 않는 케이스가 생길 수 있고, node가 다이나믹하게 변경될 수 있는 케이스에 대응이 어렵다거나 등등.
When?
아래와 같은 걸 할 수 있다.
const ref = (node) => {
node?.focus()
}
// ...
return <input ref={ref} type="text" />
const ref = (node) => {
node?.focus()
}
// ...
return <input ref={ref} type="text" />
useCallback
근데 위와 같이 하면 매 렌더시마다 포커스가 될 것이므로, 거의 대부분의 경우 useCallback
이랑 쓰게 될 것이다.
const ref = useCallback(() => {
node?.focus()
}, [])
const ref = useCallback(() => {
node?.focus()
}, [])
Further Readings
2023. 6. 25.next.js에서 route가 변경되었을 경우 이전 route를 죽지 않게 하려면
til이 딱히 til이 아닌 것 같고 github을 cms로 쓰는 것도 좀 번거로운 감이 있어서 걍 .mdx
파일을 작성하는 걸로 하고 route 이름도 notes
로 바꾸었는데, 바꾸고 보니 이전 링크를 preserve 해야 되겠다 싶어서 알아보았다. 브로큰 링크를 만들지 않아보겠다! 가 얼마 전에 목표가 되었기 때문에.
하려는 것
- (아마도 없을 것 같긴 하지만) 이전 링크를 어딘가 저장해 둔 누군가가 있을 수도 있다.
- 그 저장된 링크는
/til/:id
일 것 - 바뀐 링크는
/notes/:id
이지만, - 2.의 링크로도 액세스가 가능해야 함
그러니까 redirects
설정을 하면 되는 것인데,
next.config.js
아래와 같이 하면 된다.
/** @type {import('next').NextConfig} */
const nextConfig = {
// ...
async redirects() {
return [
// ...
{
source: "/til/:path*",
destination: "/notes/:path*",
permanent: false,
},
]
},
}
/** @type {import('next').NextConfig} */
const nextConfig = {
// ...
async redirects() {
return [
// ...
{
source: "/til/:path*",
destination: "/notes/:path*",
permanent: false,
},
]
},
}
대략 "/til/*"
하면 되지 않을까 했는데 역시 아니었고 "foo/:path*"
라구 해야 함. permanent: false
인 것에도 유의.
Ref
2023. 6. 24.app router에서 `fetch`를 쓰지 않을 경우 cache 동작
tl;dr
되긴 됨
Detail
fetch를 쓰지 않을 경우 Default Caching Behavior를 따르거나 Segment Cache Configuration를 따르게 됨.
Default Caching Behavior
If the segment is static (default), the output of the request will be cached and revalidated (if configured) alongside the rest of the segment. If the segment is dynamic, the output of the request will not be cached and will be re-fetched on every request when the segment is rendered.
Segment Cache Config
As a temporary solution, until the caching behavior of third-party queries can be configured, you can use segment configuration to customize the cache behavior of the entire segment.
like:
// app/page.tsx
import prisma from './lib/prisma'
export const revalidate = 3600 // revalidate every hour
async function getPosts() {
const posts = await prisma.post.findMany()
return posts
}
export default async function Page() {
const posts = await getPosts()
// ...
}
// app/page.tsx
import prisma from './lib/prisma'
export const revalidate = 3600 // revalidate every hour
async function getPosts() {
const posts = await prisma.post.findMany()
return posts
}
export default async function Page() {
const posts = await getPosts()
// ...
}
Ref
2023. 6. 21.EcmaScript 업데이트 상황 편하게 보기
http://kangax.github.io/compat-table/es2016plus/
약간 그... caniuse 느낌으로 보여준다.
2023. 6. 20.tailwind 👍
panda-css를 시험삼아 함 써보던 중에 쓰면서는 인식하지 못하고 있던 tailwind의 장점을 알게 됐다.
tailwind로 작성되어 있던 코드를 바꿔봤는데:
오른쪽의 형태가 되는 순간 어 이건 좀... 싶어지면서 아래의 두가지를 비로소 인식하게 되었다.
styled-components
/emotion
처럼css
syntax를 쓰던지stitches
처럼 js object syntax를 쓰던지 상관없이 스타일링 관련 코드는 무조건 세로 방향/2차원으로 코드 면적이 늘어나게 된다는 점.key: value
의 형태 때문에 반복하게 되는 타이핑량 + 시각적 스트레스가 있다는 점.
그러나 tailwind는:
- 코드가 가로/1차원으로만 늘어나고,
value
만 줄줄이 넣는 식.
이라 1,2로 알게 모르게 느끼고 있던 스트레스가 해결되고 있었다는 걸 알게 되었고 결론은 tailwind가 선택지에 있으면 나는 tailwind를 쓸 것 같다는 뭐 그런 얘기.
추가:
- 이게 코드량/면적의 문제도 있지만 스타일링 관련 코드가 다른 코드랑 '생긴게' 아예 달라진다는 점.
- 스타일링 관련 코드가
className="..."
에 '격리' 된다는 점.
을 또 들 수 있을 것 같다.
위에 나열한 요소들이 중요하게 느껴지는 건 아무래도 시각적으로 인지 부하가 줄어들기 때문인 것 같고.
2023. 6. 18.tailwind에서 pseudo 머시기 쓰기
css | tailwind(modifier) |
---|---|
:first-child | first: |
:first-of-type | first-of-type: |
:only-child | only: |
:nth-child(odd) | odd: |
:after | after: |
group
, peer
추가로 지원하고 있는 기능이 있는데,
group
parent의 상태를 child가 쓸 수 있도록 함.
예)
<a href="#" class="group">
<!-- ^^^^^ parent에서 `group`을 선언하고 나서 -->
<div class="flex items-center space-x-3">
<svg class="group-hover:stroke-white" fill="none" viewBox="0 0 24 24"><!-- ... --></svg>
<!-- ^^^^^^^^^^^^^^^^^^^^^^^^ parent의 상태를 child 스타일링에 쓸 수 잇음 -->
</div>
</a>
<a href="#" class="group">
<!-- ^^^^^ parent에서 `group`을 선언하고 나서 -->
<div class="flex items-center space-x-3">
<svg class="group-hover:stroke-white" fill="none" viewBox="0 0 24 24"><!-- ... --></svg>
<!-- ^^^^^^^^^^^^^^^^^^^^^^^^ parent의 상태를 child 스타일링에 쓸 수 잇음 -->
</div>
</a>
peer
자신의 상태를 sibling이 쓸 수 있도록 함.
<form>
<label class="block">
<input type="email" class="peer ..."/>
<!-- ^^^^ 여기서 선언하면 -->
<p class="mt-2 invisible peer-invalid:visible text-pink-600 text-sm">
<!-- ^^^^^^^^^^^^^^^^^^^^ 이렇게 sibling이 쓸 수 있음 -->
Please provide a valid email address.
</p>
</label>
</form>
<form>
<label class="block">
<input type="email" class="peer ..."/>
<!-- ^^^^ 여기서 선언하면 -->
<p class="mt-2 invisible peer-invalid:visible text-pink-600 text-sm">
<!-- ^^^^^^^^^^^^^^^^^^^^ 이렇게 sibling이 쓸 수 있음 -->
Please provide a valid email address.
</p>
</label>
</form>
주의) peer-{modifier}
는 peer
의 prev
가 아니라 next
sibling이 쓸 수 있음
Ref
2023. 6. 16.Motivation
- 신경과학적으로 'Liking'에 연관된 기관과 'Wanting'에 연관된 기관은 따로라고 한다.
- 어떤 행위를 했을 때 좋다고 느낀다고 그걸 하고 싶어지는 것은 아닌 것이 그 때문.
- 예) 어쩌다 간만에 운동을 하고 '아 역시 좋군' 한다고 다음날 아침에 '와! 어서 피트니스 센터에 가고 싶군!' 이 되지 않음
- 'Wanting' 관련 기관은 21세기의 도파민 폭탄들 때문에 맛이 가있을 확률이 높고, 그래서 현대인은 대부분 Liking/Wanting 싱크가 더 안맞고 있을 확률이 ⬆️
- 그리고 뇌는 어떤 행위를 하기 전에 그것에 드는 인풋과 돌아올 보상의 수지 타산을 늘 계산하는데, 우선 순위가 저인풋고보상>저인풋저보상>고인풋고보상>고인풋저보상 이라고.
- 대부분의 의미 있는 일은 고인풋 고보상이라 늘 우선 순위에서 밀리게 마련이고 그래서 의욕이 생기지 않는 것이라고.
- 그러나 '왜 나는 의욕이 없지?', '보조제를 먹어야 되나?', '스터디 모임을 만들어야 되나?' 등과 같이 '집나간 의욕을 어떻게 찾아올까'는 실패할 확률이 더 높다.
- 그보다 성공률이 높은 건 의욕 유무를 실행 조건에서 빼는 것.
- '해야 되는데 하고 싶지 않으니 의욕을 만들어서 한다'가 아니라 '해야 되니까 그냥 한다'가 훨씬 효과적이라고.
Refs
2023. 6. 15.JavaScript 1.2 Reference
- 나는 분명히 rsc 글을 쓰던 중이었는데
- 어쩌다보니 초창기 js 코드는 어떻게 생겼었을까 궁금해져서
- 검색도 해보고 gpt한테 물어도 보고 했는데 적절한 결과를 얻을 수 없었는데
- 그러다 아 초기 문서를 찾아보면 되겠구나 하는 생각이 들어 찾아보니
- JavaScript 1.2 Reference 가 있었다
"10/31/97" ☜ 간지 난다...
2023. 6. 15.`for (; amount--; )`
react-scrollbars-custom의 예제를 보다가 충격적인 걸 봐갖고
export function renderAmountOfParagraphs(amount = 5, paragraphsProps) {
const result = [];
for (; amount--; ) { // ??
result.push(
<p {...paragraphsProps} className="ExampleCard-Paragraph" key={`paragraph_${amount}`}>
{getRandomParagraphText()}
</p>
);
}
return result;
}
export function renderAmountOfParagraphs(amount = 5, paragraphsProps) {
const result = [];
for (; amount--; ) { // ??
result.push(
<p {...paragraphsProps} className="ExampleCard-Paragraph" key={`paragraph_${amount}`}>
{getRandomParagraphText()}
</p>
);
}
return result;
}
?
for (; amount--; ) {
for (; amount--; ) {
?? 🤯
mdn의 for 관련 문서를 보니
🤯🤯
이 외에도 충격적인 게 더 있었는데:
🤯🤯🤯
Using for
with two iterating variables
🤯🤯🤯🤯