Help
RSS
API
Feed
Maltego
Contact
Domain > www.awkwardsilence.com
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2017-11-12
50.17.240.73
(
ClassC
)
2025-01-14
3.163.24.25
(
ClassC
)
Port 80
HTTP/1.1 301 Moved PermanentlyServer: CloudFrontDate: Tue, 14 Jan 2025 13:44:16 GMTContent-Type: text/htmlContent-Length: 167Connection: keep-aliveLocation: https://www.awkwardsilence.com/X-Cache: Redirect from cloudfrontVia: 1.1 44e3ef26e727fc044d711ef45aefcd72.cloudfront.net (CloudFront)X-Amz-Cf-Pop: HIO52-P2X-Amz-Cf-Id: RQ5egR5f_o4pDygPPP-rScJZ2aN9LpmkJB11HCzzMalHANbb9s0Juw html>head>title>301 Moved Permanently/title>/head>body>center>h1>301 Moved Permanently/h1>/center>hr>center>CloudFront/center>/body>/html>
Port 443
HTTP/1.1 200 OKContent-Type: text/htmlContent-Length: 37722Connection: keep-aliveDate: Tue, 14 Jan 2025 13:44:17 GMTLast-Modified: Mon, 26 Sep 2022 22:00:26 GMTETag: e4f6b163e7f77d2a22fb789736a5ae30Accept-Ranges: bytesServer: AmazonS3X-Cache: Miss from cloudfrontVia: 1.1 44e3ef26e727fc044d711ef45aefcd72.cloudfront.net (CloudFront)X-Amz-Cf-Pop: HIO52-P2X-Amz-Cf-Id: mrvOiSBQoRsPWB1bJ57Ry00I_iUg6h1KrDyOpSI7-pjNbcNGQfm97w !DOCTYPE html>html classh-full antialiased langen>head>meta charSetutf-8/>meta nameviewport contentwidthdevice-width/>link relpreload asimage href/_next/static/media/profile-pic.a67d304b.jpg/>title>JM Presley - Software designer, crafter, and buttercream connoisseur/title>meta namedescription contentI'm JM, a software designer and crafter based in Palm Springs, California./>meta namenext-head-count content5/>script> let darkModeMediaQuery window.matchMedia((prefers-color-scheme: dark)) updateMode() darkModeMediaQuery.addEventListener(change, updateModeWithoutTransitions) window.addEventListener(storage, updateModeWithoutTransitions) function updateMode() { let isSystemDarkMode darkModeMediaQuery.matches let isDarkMode window.localStorage.isDarkMode true || (!(isDarkMode in window.localStorage) && isSystemDarkMode) if (isDarkMode) { document.documentElement.classList.add(dark) } else { document.documentElement.classList.remove(dark) } if (isDarkMode isSystemDarkMode) { delete window.localStorage.isDarkMode } } function disableTransitionsTemporarily() { document.documentElement.classList.add(&_*:!transition-none) window.setTimeout(() > { document.documentElement.classList.remove(&_*:!transition-none) }, 0) } function updateModeWithoutTransitions() { disableTransitionsTemporarily() updateMode() }/script>link relalternate typeapplication/rss+xml hrefundefined/rss/feed.xml/>link relalternate typeapplication/feed+json hrefundefined/rss/feed.json/>link relpreload href/_next/static/css/53cf55f21ad3ad0a.css asstyle/>link relstylesheet href/_next/static/css/53cf55f21ad3ad0a.css data-n-g/>noscript data-n-css>/noscript>script defer nomodule src/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js>/script>script src/_next/static/chunks/webpack-b8f8d6679aaa5f42.js defer>/script>script src/_next/static/chunks/framework-7751730b10fa0f74.js defer>/script>script src/_next/static/chunks/main-05b7b182d80246a6.js defer>/script>script src/_next/static/chunks/pages/_app-43ecf4a5fac79f9f.js defer>/script>script src/_next/static/chunks/pages/index-83e771c5d77b2bf3.js defer>/script>script src/_next/static/tFo2nPQyQTrQ8wD3WxrL7/_buildManifest.js defer>/script>script src/_next/static/tFo2nPQyQTrQ8wD3WxrL7/_ssgManifest.js defer>/script>/head>body classflex h-full flex-col bg-zinc-50 dark:bg-black>div id__next>div classfixed inset-0 flex justify-center sm:px-8>div classflex w-full max-w-7xl lg:px-8>div classw-full bg-white ring-1 ring-zinc-100 dark:bg-zinc-900 dark:ring-zinc-300/20>/div>/div>/div>div classrelative>header classrelative z-50 flex flex-col pointer-events-none styleheight:var(--header-height);margin-bottom:var(--header-mb)>div classorder-last mt-calc(theme(spacing.16)-theme(spacing.3))>/div>div classsm:px-8 top-0 order-last pt-3 -mb-3 styleposition:var(--header-position)>div classmx-auto max-w-7xl lg:px-8>div classrelative px-4 sm:px-8 lg:px-12>div classmax-w-2xl mx-auto lg:max-w-5xl>div classtop-var(--avatar-top,theme(spacing.3)) w-full styleposition:var(--header-inner-position)>div classrelative>div classabsolute left-0 transition-opacity origin-left top-3 h-10 w-10 rounded-full bg-white/90 p-0.5 shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur dark:bg-zinc-800/90 dark:ring-white/10 styleopacity:var(--avatar-border-opacity, 0);transform:var(--avatar-border-transform)>/div>a aria-labelHome classblock w-16 h-16 origin-left pointer-events-auto styletransform:var(--avatar-image-transform) href/>img alt src/_next/static/media/profile-pic.a67d304b.jpg width55 height55 decodingasync data-nimgfuture classrounded-full bg-zinc-100 object-cover dark:bg-zinc-800 h-16 w-16 stylecolor:transparent/>/a>/div>/div>/div>/div>/div>/div>div classtop-0 z-10 h-16 pt-6 styleposition:var(--header-position)>div classsm:px-8 top-var(--header-top,theme(spacing.6)) w-full styleposition:var(--header-inner-position)>div classmx-auto max-w-7xl lg:px-8>div classrelative px-4 sm:px-8 lg:px-12>div classmax-w-2xl mx-auto lg:max-w-5xl>div classrelative flex gap-4>div classflex flex-1>/div>div classflex justify-end flex-1 md:justify-center>div classpointer-events-auto md:hidden data-headlessui-state>button classflex items-center px-4 py-2 text-sm font-medium rounded-full shadow-lg group bg-white/90 text-zinc-800 shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur dark:bg-zinc-800/90 dark:text-zinc-200 dark:ring-white/10 dark:hover:ring-white/20 idheadlessui-popover-button-:Rqb6: typebutton aria-expandedfalse data-headlessui-state>Menusvg viewBox0 0 8 6 aria-hiddentrue classw-2 h-auto ml-3 stroke-zinc-500 group-hover:stroke-zinc-700 dark:group-hover:stroke-zinc-400>path dM1.75 1.75 4 4.25l2.25-2.5 fillnone stroke-width1.5 stroke-linecapround stroke-linejoinround>/path>/svg>/button>/div>nav classhidden pointer-events-auto md:block>ul classflex px-3 text-sm font-medium rounded-full shadow-lg bg-white/90 text-zinc-800 shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur dark:bg-zinc-800/90 dark:text-zinc-200 dark:ring-white/10>li>a classrelative block px-3 py-2 transition hover:text-teal-500 dark:hover:text-teal-400 href/posts>Blog/a>/li>li>a classrelative block px-3 py-2 transition hover:text-teal-500 dark:hover:text-teal-400 href/about>About/a>/li>/ul>/nav>/div>div classflex justify-end md:flex-1>div classpointer-events-auto>button typebutton aria-labelToggle dark mode classpx-3 py-2 transition rounded-full shadow-lg group bg-white/90 shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur dark:bg-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20>svg viewBox0 0 24 24 stroke-width1.5 stroke-linecapround stroke-linejoinround aria-hiddentrue classh-6 w-6 fill-zinc-100 stroke-zinc-500 transition group-hover:fill-zinc-200 group-hover:stroke-zinc-700 dark:hidden @media(prefers-color-scheme:dark):fill-teal-50 @media(prefers-color-scheme:dark):stroke-teal-500 @media(prefers-color-scheme:dark):group-hover:fill-teal-50 @media(prefers-color-scheme:dark):group-hover:stroke-teal-600>path dM8 12.25A4.25 4.25 0 0 1 12.25 8v0a4.25 4.25 0 0 1 4.25 4.25v0a4.25 4.25 0 0 1-4.25 4.25v0A4.25 4.25 0 0 1 8 12.25v0Z>/path>path dM12.25 3v1.5M21.5 12.25H20M18.791 18.791l-1.06-1.06M18.791 5.709l-1.06 1.06M12.25 20v1.5M4.5 12.25H3M6.77 6.77 5.709 5.709M6.77 17.73l-1.061 1.061 fillnone>/path>/svg>svg viewBox0 0 24 24 aria-hiddentrue classhidden h-6 w-6 fill-zinc-700 stroke-zinc-500 transition dark:block @media(prefers-color-scheme:dark):group-hover:stroke-zinc-400 @media_not_(prefers-color-scheme:dark):fill-teal-400/10 @media_not_(prefers-color-scheme:dark):stroke-teal-500>path dM17.25 16.22a6.937 6.937 0 0 1-9.47-9.47 7.451 7.451 0 1 0 9.47 9.47ZM12.75 7C17 7 17 2.75 17 2.75S17 7 21.25 7C17 7 17 11.25 17 11.25S17 7 12.75 7Z stroke-width1.5 stroke-linecapround stroke-linejoinround>/path>/svg>/button>/div>/div>/div>/div>/div>/div>/div>/div>/header>div styleheight:var(--content-offset)>/div>main>div classsm:px-8 mt-9>div classmx-auto max-w-7xl lg:px-8>div classrelative px-4 sm:px-8 lg:px-12>div classmax-w-2xl mx-auto lg:max-w-5xl>div classmax-w-2xl>h1 classtext-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl>Software designer, crafter, and buttercream connoisseur./h1>p classmt-6 text-base text-zinc-600 dark:text-zinc-400>I'm Jonmichael, but you can call me "JM". I'm a software engineer and crafter living in Southern California. This is my blog where I write about things I know, and things I'm learning./p>div classflex gap-6 mt-6>a classp-1 -m-1 group aria-labelFollow on GitHub hrefhttps://github.com/jmptr>svg viewBox0 0 24 24 aria-hiddentrue classw-6 h-6 transition fill-zinc-500 group-hover:fill-zinc-600 dark:fill-zinc-400 dark:group-hover:fill-zinc-300>path fill-ruleevenodd clip-ruleevenodd dM12 2C6.475 2 2 6.588 2 12.253c0 4.537 2.862 8.369 6.838 9.727.5.09.687-.218.687-.487 0-.243-.013-1.05-.013-1.91C7 20.059 6.35 18.957 6.15 18.38c-.113-.295-.6-1.205-1.025-1.448-.35-.192-.85-.667-.013-.68.788-.012 1.35.744 1.538 1.051.9 1.551 2.338 1.116 2.912.846.088-.666.35-1.115.638-1.371-2.225-.256-4.55-1.14-4.55-5.062 0-1.115.387-2.038 1.025-2.756-.1-.256-.45-1.307.1-2.717 0 0 .837-.269 2.75 1.051.8-.23 1.65-.346 2.5-.346.85 0 1.7.115 2.5.346 1.912-1.333 2.75-1.05 2.75-1.05.55 1.409.2 2.46.1 2.716.637.718 1.025 1.628 1.025 2.756 0 3.934-2.337 4.806-4.562 5.062.362.32.675.936.675 1.897 0 1.371-.013 2.473-.013 2.82 0 .268.188.589.688.486a10.039 10.039 0 0 0 4.932-3.74A10.447 10.447 0 0 0 22 12.253C22 6.588 17.525 2 12 2Z>/path>/svg>/a>a classp-1 -m-1 group aria-labelFollow on LinkedIn hrefhttps://www.linkedin.com/in/jmpresley/>svg viewBox0 0 24 24 classw-6 h-6 transition fill-zinc-500 group-hover:fill-zinc-600 dark:fill-zinc-400 dark:group-hover:fill-zinc-300>path dM18.335 18.339H15.67v-4.177c0-.996-.02-2.278-1.39-2.278-1.389 0-1.601 1.084-1.601 2.205v4.25h-2.666V9.75h2.56v1.17h.035c.358-.674 1.228-1.387 2.528-1.387 2.7 0 3.2 1.778 3.2 4.091v4.715zM7.003 8.575a1.546 1.546 0 01-1.548-1.549 1.548 1.548 0 111.547 1.549zm1.336 9.764H5.666V9.75H8.34v8.589zM19.67 3H4.329C3.593 3 3 3.58 3 4.297v15.406C3 20.42 3.594 21 4.328 21h15.338C20.4 21 21 20.42 21 19.703V4.297C21 3.58 20.4 3 19.666 3h.003z>/path>/svg>/a>/div>/div>/div>/div>/div>/div>div classsm:px-8 mt-24 md:mt-28>div classmx-auto max-w-7xl lg:px-8>div classrelative px-4 sm:px-8 lg:px-12>div classmax-w-2xl mx-auto lg:max-w-5xl>div classgrid max-w-xl grid-cols-1 mx-auto>div classflex flex-col gap-16>article classgroup relative flex flex-col items-start>h2 classtext-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100>div classabsolute z-0 transition scale-95 opacity-0 -inset-y-6 -inset-x-4 bg-zinc-50 group-hover:scale-100 group-hover:opacity-100 dark:bg-zinc-800/50 sm:-inset-x-6 sm:rounded-2xl>/div>a href/posts/2020-04-25-css-variables>span classabsolute z-20 -inset-y-6 -inset-x-4 sm:-inset-x-6 sm:rounded-2xl>/span>span classrelative z-10>CSS Variables in media queries/span>/a>/h2>time classrelative z-10 order-first mb-3 flex items-center text-sm text-zinc-400 dark:text-zinc-500 pl-3.5 dateTime2020-04-25>span classabsolute inset-y-0 left-0 flex items-center aria-hiddentrue>span classh-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500>/span>/span>April 25, 2020/time>p classrelative z-10 mt-2 text-sm text-zinc-600 dark:text-zinc-400>CSS variables are a powerful way to build dynamic styles. Here are some tips for using CSS variables with media queries./p>div aria-hiddentrue classrelative z-10 flex items-center mt-4 text-sm font-medium text-teal-500>Read articlesvg viewBox0 0 16 16 fillnone aria-hiddentrue classw-4 h-4 ml-1 stroke-current>path dM6.75 5.75 9.25 8l-2.5 2.25 stroke-width1.5 stroke-linecapround stroke-linejoinround>/path>/svg>/div>/article>article classgroup relative flex flex-col items-start>h2 classtext-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100>div classabsolute z-0 transition scale-95 opacity-0 -inset-y-6 -inset-x-4 bg-zinc-50 group-hover:scale-100 group-hover:opacity-100 dark:bg-zinc-800/50 sm:-inset-x-6 sm:rounded-2xl>/div>a href/posts/2020-04-21-improving-the-local-storage-hook>span classabsolute z-20 -inset-y-6 -inset-x-4 sm:-inset-x-6 sm:rounded-2xl>/span>span classrelative z-10>Improving the local storage react hook using TypeScript/span>/a>/h2>time classrelative z-10 order-first mb-3 flex items-center text-sm text-zinc-400 dark:text-zinc-500 pl-3.5 dateTime2020-04-21>span classabsolute inset-y-0 left-0 flex items-center aria-hiddentrue>span classh-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500>/span>/span>April 21, 2020/time>p classrelative z-10 mt-2 text-sm text-zinc-600 dark:text-zinc-400>Using local storage in react web applications has some caveats that we'll explore before learning how we can use TypeScript to create an improved useLocalStorage hook./p>div aria-hiddentrue classrelative z-10 flex items-center mt-4 text-sm font-medium text-teal-500>Read articlesvg viewBox0 0 16 16 fillnone aria-hiddentrue classw-4 h-4 ml-1 stroke-current>path dM6.75 5.75 9.25 8l-2.5 2.25 stroke-width1.5 stroke-linecapround stroke-linejoinround>/path>/svg>/div>/article>article classgroup relative flex flex-col items-start>h2 classtext-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100>div classabsolute z-0 transition scale-95 opacity-0 -inset-y-6 -inset-x-4 bg-zinc-50 group-hover:scale-100 group-hover:opacity-100 dark:bg-zinc-800/50 sm:-inset-x-6 sm:rounded-2xl>/div>a href/posts/2018-06-22-3-generations-of-flatten>span classabsolute z-20 -inset-y-6 -inset-x-4 sm:-inset-x-6 sm:rounded-2xl>/span>span classrelative z-10>3 Generations of Flatten in JavaScript/span>/a>/h2>time classrelative z-10 order-first mb-3 flex items-center text-sm text-zinc-400 dark:text-zinc-500 pl-3.5 dateTime2018-06-22>span classabsolute inset-y-0 left-0 flex items-center aria-hiddentrue>span classh-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500>/span>/span>June 22, 2018/time>p classrelative z-10 mt-2 text-sm text-zinc-600 dark:text-zinc-400>/p>div aria-hiddentrue classrelative z-10 flex items-center mt-4 text-sm font-medium text-teal-500>Read articlesvg viewBox0 0 16 16 fillnone aria-hiddentrue classw-4 h-4 ml-1 stroke-current>path dM6.75 5.75 9.25 8l-2.5 2.25 stroke-width1.5 stroke-linecapround stroke-linejoinround>/path>/svg>/div>/article>/div>/div>/div>/div>/div>/div>/main>footer classmt-32>div classsm:px-8>div classmx-auto max-w-7xl lg:px-8>div classpt-10 pb-16 border-t border-zinc-100 dark:border-zinc-700/40>div classrelative px-4 sm:px-8 lg:px-12>div classmax-w-2xl mx-auto lg:max-w-5xl>div classflex flex-col items-center justify-between gap-6 sm:flex-row>div classflex gap-6 text-sm font-medium text-zinc-800 dark:text-zinc-200>a classtransition hover:text-teal-500 dark:hover:text-teal-400 href/posts>Blog/a>a classtransition hover:text-teal-500 dark:hover:text-teal-400 href/about>About/a>/div>p classtext-sm text-zinc-400 dark:text-zinc-500>© !-- -->2022!-- --> Jon "JM" Presley. All rights reserved./p>/div>/div>/div>/div>/div>/div>/footer>/div>/div>script id__NEXT_DATA__ typeapplication/json>{props:{pageProps:{articles:{title:CSS Variables in media queries,date:2020-04-25,description:CSS variables are a powerful way to build dynamic styles. Here are some tips for using CSS variables with media queries.,cover_photo:../../photos/jamie-street-_94HLr_QXo8-unsplash.jpg,attribution:Photo by Jamie Street on Unsplash,content:\r\nCSS properties, known as CSS variables, allow developers to bind key-value pairs to an HTML element. These key-value pairs can be accessed by any ancestor of the bound element. When combined with media queries, a style sheet can allow for more consistent themes and layouts.\r\n\r\n\u003e Its worth noting that CSS variables are not supported by every use case. Check caniusecaniuse for more information about whether your use case is supported.\r\n\r\n## Using a CSS variable\r\n\r\nThe format for _declaring_ a CSS variable uses the same syntax as declaring a value for any other CSS property, but it _must_ be prefixed by double-hyphens `--`. The name can include numbers, letters, and hyphens. Letters in the name can be mixed-case. Here are some examples of valid CSS variables:\r\n\r\n```CSS\r\n:root {\r\n --blue: #0d6efd;\r\n --font-size-base: 2rem;\r\n\r\n ---leading-hyphen: pink;\r\n ---mixedCase: red;\r\n --font-700-bold: 700;\r\n}\r\n```\r\n\r\n### Consuming a CSS variable\r\n\r\nConsuming a CSS variable is pretty straight-forward. A variable can be used in place of any part of any value in any property on an element. It _cant_ be used as a property _name_.\r\n\r\n```CSS\r\np {\r\n /* valid usage */\r\n --font-size-base: 2rem;\r\n font-size: var(--font-size-base);\r\n\r\n /* invalid usage */\r\n --prop: color;\r\n var(--prop): red;\r\n}\r\n```\r\n\r\n### Composition with CSS variables\r\n\r\nAs mentioned, CSS variables can be used in _any_ part of _any_ value. This allows composition of multiple values.\r\n\r\n```CSS\r\np {\r\n --border-color: black;\r\n --border-width: 1px;\r\n border: var(--border-width) solid var(--border-color);\r\n /* border: 1px solid black; */\r\n}\r\n```\r\n\r\n### Additional behaviors\r\n\r\nInterestingly, CSS variables can be defined anywhere in the body of an element:\r\n\r\n```CSS\r\np {\r\n color: var(--color);\r\n --color: black;\r\n /* color: black; */\r\n}\r\n```\r\n\r\nCSS variables can also be defined multiple times, but the last defined variable is used:\r\n\r\n```CSS\r\np {\r\n color: var(--color);\r\n --color: black;\r\n --color: red;\r\n /* color: red; */\r\n}\r\n```\r\n\r\nCSS variables wont prevent invalid values. The browser will try to interpret the value, but may not have an implementation for the value:\r\n\r\n```CSS\r\np {\r\n --font-size-base: blerb;\r\n font-size: var(--font-size-base);\r\n /* font-size: blerb; */\r\n}\r\n```\r\n\r\nFinally, variables can refer to each other, but can also compute to a guaranteed invalid value:\r\n\r\n```CSS\r\np {\r\n --one: calc(var(--two) + 20px);\r\n --two: calc(var(--one) - 20px);\r\n font-size: var(--two);\r\n /* font-size: calc(var(--one) - 20px); */\r\n /* invalid value */\r\n}\r\n```\r\n\r\n## Media Query interactions\r\n\r\nMedia queriesmediaquery are a CSS feature that help cascade styles based on media conditions. They are used often to provide responsive layouts for web applications.\r\n\r\nHeres an example of a media query that changes font size on a paragraph as the width of the browser increases:\r\n\r\n```CSS\r\np {\r\n --font-size: 2rem;\r\n font-size: var(--font-size);\r\n}\r\n\r\n@media (min-width: 500px) {\r\n p {\r\n font-size: calc(var(--font-size) * 2);\r\n }\r\n}\r\n\r\n@media (min-width: 1000px) {\r\n p {\r\n font-size: calc(var(--font-size) * 3);\r\n }\r\n}\r\n```\r\n\r\n### Using `var()` in the media query\r\n\r\nUnfortunately, since `var()` can only be used to compute the _value_ of a CSS property, it cant be used in the media query itself. This example would not affect the page layout:\r\n\r\n```CSS\r\n:root {\r\n --sm: 500px;\r\n --md: 1000px;\r\n}\r\np {\r\n max-width: 100%;\r\n}\r\n\r\n@media (min-width: var(--sm)) {\r\n p {\r\n max-width: 90%;\r\n }\r\n}\r\n\r\n@media (min-width: var(--md)) {\r\n p {\r\n max-width: 75%;\r\n }\r\n}\r\n```\r\n\r\n## Conclusions\r\n\r\nWhile there are some compatibility issues to consider, CSS variables using the `var()` function are a powerful way to create dynamic components for web applications. Combining the power of CSS variables with media query, one can implement dynamic behavior based on all manner of queries.\r\n\r\nBy combining CSS variables with media query, an application can be optimized for all manner of screen sizes and orientations, dark mode themes, even some sensor-based queries like `light-level`lightlevel.\r\n\r\n## References\r\n\r\n- caniuse CSS Variablescaniuse shows browser compatibility.\r\n- CSS Properties speccsspropertiesspec.\r\n- MDN Using media queriesmediaquery.\r\n\r\ncaniuse: https://caniuse.com/#featcss-variables\r\ncsspropertiesspec: https://drafts.csswg.org/css-variables/#defining-variables\r\nmediaquery: https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries\r\nlightlevel: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/light-level\r\n,slug:2020-04-25-css-variables,href:posts/2020-04-25-css-variables},{title:Improving the local storage react hook using TypeScript,date:2020-04-21,description:Using local storage in react web applications has some caveats that well explore before learning how we can use TypeScript to create an improved useLocalStorage hook.,cover_photo:../../photos/lucas-sankey-1i7RLrsEcZo-unsplash.jpg,attribution:Photo by Lucas Sankey on Unsplash,content:\r\n`localStorage`localstorage is a browser technology that allows an application to save data in key-value pairs. When used with react hooksreacthooks, a web application can subscribe to a value in `localStorage`. Lets look at a common implementation of `useLocalStorage` and see how it can be improved.\r\n\r\n## The common `useLocalStorage` hook\r\n\r\nThe common `useLocalStorage` hook wraps `localStorage.getItem` with a `useState` initializer and returns a tuple with the value and a value setter. Here is an implementation of the common `useLocalStorage` hook:\r\n\r\n```ts\r\nconst useLocalStorage (key: string) \u003e {\r\n // initialize the value from localStorage\r\n const currentValue, setCurrentValue useState\u003cstring | null\u003e(() \u003e\r\n localStorage.getItem(key)\r\n );\r\n\r\n // update localStorage when the currentValue changes via setCurrentValue\r\n useEffect(() \u003e {\r\n localStorage.setItem(key, currentValue);\r\n }, key, currentValue);\r\n\r\n // use as const to tell TypeScript this is a tuple\r\n return currentValue, setCurrentValue as const;\r\n};\r\n```\r\n\r\nThis works for the majority of use cases. However, there is a use case where another tab can change the underlying value stored by `localStorage`. When this happens, the application wont be using the value in storage, it will be using the value stored _in memory_. Heres an explanation from the MDN documentsstorageevent (emphasis mine):\r\n\r\n\u003e The `storage` event of the `Window` interface fires when a storage area (`localStorage` or `sessionStorage`) has been modified in the _context of another document_.\r\n\r\nNext, lets look at how we can keep our components updated by consuming the this event.\r\n\r\n## An improved useLocalStorage hook\r\n\r\nIn order to inform our component that a property in `localStorage` has been updated, we need to subscribe to the `storage` event exposed by the `window`. The emitted event object has `storageArea`, `key`, and `newValue` properties that can be used to determine whether the hook should inform consumers of the change. By utilizing the `useEffect` hook, we can set up a subscription to the `storage` event that obeys the react lifecycle.\r\n\r\n```ts\r\nconst useLocalStorage (key: string) \u003e {\r\n // initialize the value from localStorage\r\n const currentValue, setCurrentValue useState\u003cstring | null\u003e(() \u003e\r\n localStorage.getItem(key)\r\n );\r\n\r\n // on every render, re-subscribe to the storage event\r\n useEffect(() \u003e {\r\n const handler (e: StorageEvent) \u003e {\r\n if (e.storageArea localStorage \u0026\u0026 e.key key) {\r\n setCurrentValue(e.newValue);\r\n }\r\n };\r\n window.addEventListener(storage, handler);\r\n return () \u003e {\r\n window.removeEventListener(storage, handler);\r\n };\r\n });\r\n\r\n // update localStorage when the currentValue changes via setCurrentValue\r\n useEffect(() \u003e {\r\n localStorage.setItem(key, currentValue);\r\n }, key, currentValue);\r\n\r\n // use as const to tell TypeScript this is a tuple\r\n return currentValue, setCurrentValue as const;\r\n};\r\n```\r\n\r\nNow that our component subscribes to changes to the `localStorage` property, there is an opportunity to create a new, reusable hook! Since our new `useLocalStorage` hook is subscribing to a _specific_ window event, we can make a hook that wraps any arbitrary window event.\r\n\r\n## The `useWindowEvent` hook\r\n\r\nThe `window` object has a large number of events that it supports. By using TypeScript and loading the `dom` librarylibdom, we can access the entire series of events and provide a type-safe way to handle any event defined in the `WindowEventMap`. This technique will require us to apply a _generic_ type parameter that links to the `WindowEventMap`.\r\n\r\n```ts\r\ninterface EventHandler\u003cT extends Event Event\u003e {\r\n (e: T): void;\r\n}\r\n\r\ninterface WindowEventHook {\r\n \u003cK extends keyof WindowEventMap\u003e(\r\n eventName: K,\r\n handler: EventHandler\u003cWindowEventMapK\u003e\r\n ): void;\r\n}\r\n\r\nconst useWindowEvent: WindowEventHook (eventName, handler) \u003e {\r\n // optimization: using useRef here helps us guarantee that this function is\r\n // is only mutated during effect lifecycles, adding some assurance that the\r\n // function invoked by the event listener is the same function passed to the\r\n // hook.\r\n const handlerRef useRef\u003ctypeof handler\u003e();\r\n\r\n useEffect(() \u003e {\r\n handlerRef.current handler;\r\n }, handler);\r\n\r\n useEffect(() \u003e {\r\n const eventListener: typeof handler event \u003e handlerRef.current(event);\r\n window.addEventListener(eventName, eventListener);\r\n\r\n return () \u003e {\r\n window.removeEventListener(eventName, eventListener);\r\n };\r\n }, eventName);\r\n};\r\n\r\nconst useLocalStorage (key: string) \u003e {\r\n // initialize the value from localStorage\r\n const currentValue, setCurrentValue useState\u003cstring | null\u003e(() \u003e\r\n localStorage.getItem(key)\r\n );\r\n\r\n const handler (e: StorageEvent) \u003e {\r\n if (\r\n e.storageArea localStorage \u0026\u0026\r\n e.key key \u0026\u0026\r\n e.newValue ! currentValue\r\n ) {\r\n setCurrentValue(e.newValue);\r\n }\r\n };\r\n\r\n // set up the event listener\r\n useWindowEvent(storage, handler);\r\n\r\n // update localStorage when the currentValue changes via setCurrentValue\r\n useEffect(() \u003e {\r\n localStorage.setItem(key, currentValue);\r\n }, key, currentValue);\r\n\r\n // use as const to tell TypeScript this is a tuple\r\n return currentValue, setCurrentValue as const;\r\n};\r\n```\r\n\r\nHere, weve introduced an interface `WindowEventHook` that defines a default function `\u003cK extends keyof WindowEventMap\u003e(eventName: K, handler: EventHandler\u003cWindowEventMapK\u003e): void;`. This definition can be read in three parts:\r\n\r\n- `\u003cK extends keyof WindowEventMap\u003e` Some input `K` must be a key of the `WindowEventMap` type. This will be the basis for how we create a relationship between arguments in the next section of the function definition.\r\n- `(eventName: K, handler: EventHandler\u003cWindowEventMapK\u003e)` The function signature takes arguments `K` and an `EventHandler` of type `WindowEventMapK`.\r\n- `: void;` The function has no meaningful return value.\r\n\r\nOne might notice the use of the built-in hook `useRef` to hold a reference to the `handler` argument. This is done purely as an optimization to provide some assurance that the _reference_ to the `handler` doesnt change outside of the react component lifecycle.\r\n\r\nIf `useRef` is useful for keeping references to functions, why doesnt the second (that uses `window.addEventListener` directly) version of `useLocalStorage` also use `useRef`? This is because `useLocalStorage` uses `addEventListener` directly, the reference to the handler never changes, so `useRef` is not useful here.\r\n\r\nWith the `useWindowEvent` hook in place, the updated `useLocalStorage` hook can consume it in a type-safe way. Here is the type information revealed when consuming the `storage` event:\r\n\r\n```ts\r\nconst useWindowEvent: WindowEventHook \u003c\storage\\u003e(\r\n eventName: \storage\,\r\n handler: EventHandler\u003cStorageEvent\u003e\r\n) \u003e void\r\n```\r\n\r\nIf the user tries to subscribe to an event that doesnt exist, TypeScript will throw a compilation error. Heres an example:\r\n\r\n```ts\r\nuseWindowEvent(foo, () \u003e {});\r\n// Argument of type \foo\ is not assignable to parameter of type etc...\r\n```\r\n\r\nThis is TypeScripts way of saying \the string `foo` cant be used as an argument here because it is not found in `WindowEventMap`.\ Since we created the type constraint `\u003cK extends keyof WindowEventMap\u003e`, and our `K` argument isnt found, TypeScript cant complete the type inference needed to compile the `handler` argument.\r\n\r\n## Testing the `useWindowEvent` hook\r\n\r\nTesting react hooks using `@testing-library/react`testinglibraryreact is generally a great way to test react components. The best way to utilize this library to test our hook is to create an _integration_ test that allows us to test how our hook is consumed, and how a consumer responds to its side-effects.\r\n\r\n```tsx\r\nimport React, { useState } from react;\r\nimport { render, fireEvent } from @testing-library/react;\r\n\r\nconst Example () \u003e {\r\n const count, setCount useState(0);\r\n useWindowEvent(click, () \u003e {\r\n setCount(count + 1);\r\n });\r\n\r\n return \u003cspan data-testid\click-count\\u003e{count}\u003c/span\u003e;\r\n};\r\n\r\ntest(`Example renders the number of clicks`, async () \u003e {\r\n const { getByTestId } render(\u003cExample /\u003e);\r\n const elem getByTestId(click-count);\r\n expect(elem.textContent).toBe(0);\r\n\r\n fireEvent.click(window);\r\n expect(elem.textContent).toBe(1);\r\n\r\n fireEvent.click(window);\r\n expect(elem.textContent).toBe(2);\r\n});\r\n```\r\n\r\nIn this example, a purpose-specific component is used to test the activity of the `useWindowEvent` hook. By using the `fireEvent` API, user interactions are simulated in such a way that we can create fluid user scenarios, and the `testing-library` utilities will keep the component tree updated throughout the test.\r\n\r\n## Conclusions\r\n\r\nReact hooks are a powerful mechanism for abstracting logic away from components. In the case of `localStorage`, we found a way to improve the logic inside the hook, allowing us to avoid changes to the consuming component. We then found a way to reuse the logic in interesting ways by creating the `useWindowEvent` hook, and learned how to test the hook by using the `testing-library` for react.\r\n\r\nOne idea that occurred to me while writing this post is the idea of an even more generic `useDOMEvent` hook. While implementing the `useDOMEvent` hook, I found complications in creating the correct TypeScript overrides that would be compatible with the react hooks lifecycle. This would be an interesting challenge for another time!\r\n\r\n## Resources\r\n\r\n- react-dom-event-hooks repositorydomeventhooks is a repository where the code in this post was developed.\r\n- react-usereactuserepo has examples of react hooks that inspired this post.\r\n- useHooksBlogusehooksblog has an example of a `useLocalStorage` hook that inspired this post.\r\n\r\nlocalstorage: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage\r\nstorageevent: https://developer.mozilla.org/en-US/docs/Web/API/Window/storage_event\r\nreacthooks: https://reactjs.org/docs/hooks-intro.html\r\nreactuserepo: https://github.com/streamich/react-use\r\nusehooksblog: https://usehooks.com/useLocalStorage/\r\nlibdom: https://github.com/microsoft/TypeScript/blob/master/lib/lib.dom.d.ts\r\nmdnwindow: https://developer.mozilla.org/en-US/docs/Web/API/Window\r\neventtarget: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget\r\ntestinglibraryreact: https://testing-library.com/docs/react-testing-library/intro\r\ntestinglibraryhooks: https://github.com/testing-library/react-hooks-testing-library\r\ndomeventhooks: https://github.com/jmptr/react-dom-event-hooks\r\n,slug:2020-04-21-improving-the-local-storage-hook,href:posts/2020-04-21-improving-the-local-storage-hook},{title:3 Generations of Flatten in JavaScript,date:2018-06-22,cover_photo:../../photos/antoine-dautry-05A-kdOH6Hw-unsplash.jpg,attribution:Photo by Antoine Dautry on Unsplash,content:\r\nOne of my favorite math instructors taught me to challenge myself to solve my math problems in 3 ways: with a function, with a graph, and with a set of values. Now that JavaScript has 3 relatively recent versions, you can challenge yourself to solve problems in JavaScript using ES3, ES5, and ES6.\r\n\r\nAt first glance, it might not make sense to try using older versions of JavaScript, especially now that ES5 enjoys such high levels of support. But I think theres still a lot of value in knowing how to read and use the syntax from old versions because our ecosystem of transpilers can sometimes hide the fact that the code we write isnt always the code we use.\r\n\r\nLets take a look at an ES3 version of flatten:\r\n\r\n\u003cp class\codepen\ data-height\265\ data-theme-id\light\ data-default-tab\js,result\ data-user\jmptr\ data-slug-hash\vrjZjd\ style\height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;\ data-pen-title\flatten es3\\u003e\r\n \u003cspan\u003eSee the Pen \u003ca href\https://codepen.io/jmptr/pen/vrjZjd\\u003e\r\n flatten es3\u003c/a\u003e by JM Presley (\u003ca href\https://codepen.io/jmptr\\u003e@jmptr\u003c/a\u003e)\r\n on \u003ca href\https://codepen.io\\u003eCodePen\u003c/a\u003e.\u003c/span\u003e\r\n\u003c/p\u003e\r\n\u003cscript async src\https://static.codepen.io/assets/embed/ei.js\\u003e\u003c/script\u003e\r\n\r\nWhat I found interesting here is that Array.isArray() and Array.prototype.reduce() dont exist yet. Without access to the ES5 Array improvements, JavaScript has a hard time revealing itself as a functional programming language.\r\n\r\nThe ES5 version of flatten will look much more familiar:\r\n\r\n\u003cp class\codepen\ data-height\265\ data-theme-id\light\ data-default-tab\js,result\ data-user\jmptr\ data-slug-hash\PaaZmL\ style\height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;\ data-pen-title\flatten es5\\u003e\r\n \u003cspan\u003eSee the Pen \u003ca href\https://codepen.io/jmptr/pen/PaaZmL\\u003e\r\n flatten es5\u003c/a\u003e by JM Presley (\u003ca href\https://codepen.io/jmptr\\u003e@jmptr\u003c/a\u003e)\r\n on \u003ca href\https://codepen.io\\u003eCodePen\u003c/a\u003e.\u003c/span\u003e\r\n\u003c/p\u003e\r\n\u003cscript async src\https://static.codepen.io/assets/embed/ei.js\\u003e\u003c/script\u003e\r\n\r\nHaving access to reduce makes the function much more compact, but now I miss my arrow functions.\r\nLets see how ES6 can flatten (see this great article by Casey Morrises6recursive):\r\n\r\nDoesnt that feel nice? Having access to arrow functions and the spread operator allows us to create code that minimizes syntax noise. Does it increase or decrease readability? I think it can be difficult to read at first, but I also realize that it represents a pattern that I can apply in a number of places.\r\n\r\nThe recursion technique is neat and all, but it might be a little too neat. As Ryan Dahl recently said \I regret trying to do cute things.\ Lets look at an ES6 example that just uses arrow functions:\r\n\r\n\u003cp class\codepen\ data-height\265\ data-theme-id\light\ data-default-tab\js,result\ data-user\jmptr\ data-slug-hash\qKKbPx\ style\height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;\ data-pen-title\flatten es6\\u003e\r\n \u003cspan\u003eSee the Pen \u003ca href\https://codepen.io/jmptr/pen/qKKbPx\\u003e\r\n flatten es6\u003c/a\u003e by JM Presley (\u003ca href\https://codepen.io/jmptr\\u003e@jmptr\u003c/a\u003e)\r\n on \u003ca href\https://codepen.io\\u003eCodePen\u003c/a\u003e.\u003c/span\u003e\r\n\u003c/p\u003e\r\n\u003cscript async src\https://static.codepen.io/assets/embed/ei.js\\u003e\u003c/script\u003e\r\n\r\nThis one ultimately feels the most expressive and readable to me.\r\n\r\nOnce I had a good idea of how I wanted to express my flatten variants, I set up a JSPerf test to see how they compare in terms of performancejsperf. After running the test a few times on Chrome 67, I found that the ES3 version was ever-so-slightly faster.\r\n\r\nI guess this gives me something to think about when Im targeting a run-time version of JavaScript for my transpilers!\r\n\r\nes6recursive: https://medium.com/dailyjs/functional-js-with-es6-recursive-patterns-b7d0813ef9e3\r\njsperf: https://jsperf.com/flatten-comparison\r\n,slug:2018-06-22-3-generations-of-flatten,href:posts/2018-06-22-3-generations-of-flatten}},__N_SSG:true},page:/,query:{},buildId:tFo2nPQyQTrQ8wD3WxrL7,isFallback:false,gsp:true,scriptLoader:}/script>/body>/html>
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]