Chunk Creations

Crafting digital experiences

Trusted by

Our insights

#articles

Lessons Learned: NPM packages publishing and API authorization

While refactoring @creation-ui/react to version 15, I tried to publish a new version to npm locally and couldn’t due to NPM auth errors. Here is how it went: $ npm publish npm WARN ignoring workspace config at /Users/username/GitHub/creation-ui-react/packages/ui/.npmrc npm notice npm notice 📦 @creation-ui/react@15.0.0 npm notice === Tarball Contents === npm notice 2.1kB CHANGELOG.md npm notice 1.1kB LICENSE npm notice 1.8kB README.md npm notice 918B dist/index.css npm notice 50.0kB dist/index.d.mts npm notice 50.0kB dist/index.d.ts npm notice 355.1kB dist/index.js npm notice 1.0MB dist/index.js.map npm notice 347.2kB dist/index.mjs npm notice 1.0MB dist/index.mjs.map npm notice 1.3kB dist/theme.css npm notice 2.2kB package.json npm notice === Tarball Details === npm notice name: @creation-ui/react npm notice version: 15.0.1 npm notice filename: creation-ui-react-15.0.0.tgz npm notice package size: 547.9 kB npm notice unpacked size: 2.9 MB npm notice shasum: 769ad59ba78310274a8a34c0933cad161f2ad483 npm notice integrity: sha512-rxDCX60g0xg5Z[…]dQiQwVoyqPIOg== npm notice total files: 12 npm notice npm notice Publishing to https://registry.npmjs.org with tag latest and public access npm ERR! code E404 npm ERR! 404 Not Found - PUT https://registry.npmjs.org/@creation-ui%2freact - Not found npm ERR! 404 npm ERR! 404 '@creation-ui/react@15.0.0' is not in this registry. npm ERR! 404 npm ERR! 404 Note that you can also install from a npm ERR! 404 tarball, folder, http url, or git url. npm ERR! A complete log of this run can be found in: /Users/username/.npm/_logs/2025–03–27T14_55_04_665Z-debug-0.log First things first When I started working with the NPM API a few years back, I learned that 404 is a bit misleading, as in fact it is 401 Unauthorized. The whole 401 as 404 thing in systems is really a design decision where any resource that hasn’t been found with the current privilege level is indeed a not-found resource, resulting in the 404 error code. It is a common design pattern in the frontend: accessing /user-is-private-route while not logged in? “Well, we didn’t find such a route, sorry mate ¯\\\_(ツ)\_/¯” Everywhere else it’s a bit too close to “security through obscurity” for my liking. But I digress. The important thing is the NPM said, You Shall Not Pass! or as cool kids say 401 Not Authorized. This happened despite that I had a token set up in .env that .npmrc file should catch on. I knew it could be done with an extra CLI command, but I couldn’t be bothered to check it out. # .npmrc auto-install-peers=true strict-peer-dependencies=false @creation-ui:registry=https://registry.npmjs.org //registry.npmjs.org/:_authToken=${NPM_TOKEN_AUTOMATION} To Auth or Not to Auth But that wasn’t the only way to get authorized. So I tried thenpm login. After successfully logging in on a browser, I still got the same error message. Bugged out as I was, it suddenly occurred to me that I can actually check if I’m logged in by running the `npm whoami` command: username@machine ui % npm whoami npm WARN ignoring workspace config at /Users/username/GitHub/creation-ui-react/packages/ui/.npmrc npm ERR! code E401 npm ERR! 401 Unauthorized - GET https://registry.npmjs.org/-/whoami npm ERR! A complete log of this run can be found in: /Users/username/.npm/_logs/2025–03–27T14_55_31_687Z-debug-0.log npm ERR! code E401 npm ERR! Unauthorized - please log in to your npm account to publish this package. Crazy right? At that point I knew that: - ✅ I logged in successfully - ❌ NPM API says I’m not logged in - ❌ NPM_TOKEN_AUTOMATION is a undefined It was clear to me that how NPM auth works is not clear at all. It seemed that NPM ignores which user is logged in and takes _authToken from the .npmrc file first. After closer inspection I figured out that token at time had value set to ”${NPM_TOKEN_AUTOMATION}” because of how I set up my .npmrc file. Removing this line resolved the issue for me: //registry.npmjs.org/:_authToken=${NPM_TOKEN_AUTOMATION} After that I was able to publish my package to NPM. Conclusion Where are the docs for that, @npm!? 👀 Originally published at https://www.pawelkrystkiewicz.pl on March 27, 2025.
PK

Paweł Krystkiewicz

March 27, 2025

Tailwind 4 Dark Mode Dynamic Theme

Learn how to implement a dynamic dark mode theme in Tailwind CSS 4 using the new theme in CSS config. January 25, 2025 So the Tailwind 4 was released. The biggest breaking change was the introduction of new theme configuration method—you can only do it in CSS now. Here is an official example: @import 'tailwindcss'; @theme { --font-display: 'Satoshi', 'sans-serif'; --breakpoint-3xl: 1920px; --color-avocado-100: oklch(0.99 0 0); --color-avocado-200: oklch(0.98 0.04 113.22); --color-avocado-300: oklch(0.94 0.11 115.03); --color-avocado-400: oklch(0.92 0.19 114.08); --color-avocado-500: oklch(0.84 0.18 117.33); --color-avocado-600: oklch(0.53 0.12 118.34); --ease-fluid: cubic-bezier(0.3, 0, 0, 1); --ease-snappy: cubic-bezier(0.2, 0, 0, 1); /* ... */ } The problem with this approach is that there is no easy way to define the dark mode theme here, as complained on their GitHub. Solution This is how I solved this issue in my React components system, @creation-ui/react using @variant directive: @import 'tailwindcss'; @variant dark (&:where(.dark, .dark *)); :root { --text-primary: oklch(0 0 0); --text-secondary: oklch(0.556 0 0); --background-primary: oklch(0.99 0 0); --background-secondary: oklch(0.985 0 0); --border: oklch(0.87 0 0); @variant dark { --text-primary: oklch(0.97 0 0); --text-secondary: oklch(0.87 0 0); --background-primary: oklch(0.269 0 0); --background-secondary: oklch(0.371 0 0); --border: oklch(0.35 0 0); } }@theme { --color-primary: oklch(60.48% 0.2165 257.21); --color-warning: oklch(77.97% 0.1665 72.45); --color-error: oklch(66.16% 0.2249 25.88); --color-success: oklch(75.14% 0.1514 166.5); --color-text-primary: var(--text-primary); --color-text-secondary: var(--text-secondary); --color-background-primary: var(--background-primary); --color-background-secondary: var(--background-secondary); --color-border: var(--border); } Basically in :root we define both our light and dark color variables, where @variant dark decides of the variable values. Because Tailwind generates color CSS classes (text-[color]-value, bg-[color]-value, border-[color]-value, etc.) from --color- variables, we can use them in our React components like so: const Button = ({ children, ...props }) => { return ( ) } And that’s it! Now you can use a dynamic light/dark theme in your app, and it will automatically switch to dark mode when you set dark variant (here: a .dark class on, e.g. body element). Originally published at pawelkrystkiewicz.pl 25, 2025.
PK

Paweł Krystkiewicz

January 25, 2025

Building a Smart Truncation Detection Hook for React

Learn how to create a reusable React hook that detects text truncation in UI elements, enabling intelligent tooltips and responsive design adjustments The Truncation Detection Problem In modern UIs, we often truncate text with CSS when it exceeds container bounds: .truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } This is cool and all, but sometimes we need to keep those truncated strings helpful and communicative for our users. To that end, it would be helpful to know whether our string has been truncated or not. This knowledge could open such opportunities as This knowledge could open such opurtunites as: Showing tooltips only when content is truncated Dynamically adjusting layouts Providing expand/collapse functionality Can we actually detect that? Yes, yes we can! A very rudimentary attempt could be made by checking element dimensions: const isTruncated = element.scrollWidth > element.clientWidth While this works OK, it has several limitations: Doesn’t respond to window resizing Requires “manual” DOM access Definitely lacks React lifecycle awareness Doesn’t handle edge cases (like flex containers) To make this work the best with React, we definitely could use a hook. Solution For this to work, we need a hook with: Type safety with generics ResizeObserver for responsiveness Simple API import { RefObject, useEffect, useRef, useState } from 'react' interface UseDetectedTruncation { ref: RefObject isTruncated: boolean }export const useDetectedTruncation = < RefType extends HTMLElement, >(): UseDetectedTruncation => { const [isTruncated, setIsTruncated] = useState(false) const elementRef = useRef(null) const checkTruncation = () => { const element = elementRef.current if (!element) return // Check both width and height for multi-line truncation const isWidthTruncated = element.scrollWidth > element.clientWidth const isHeightTruncated = element.scrollHeight > element.clientHeight setIsTruncated(isWidthTruncated || isHeightTruncated) } useEffect(() => { const element = elementRef.current if (!element) return // Initial check checkTruncation() // Set up observation const resizeObserver = new ResizeObserver(checkTruncation) resizeObserver.observe(element) // MutationObserver for content changes const mutationObserver = new MutationObserver(checkTruncation) mutationObserver.observe(element, { childList: true, subtree: true, characterData: true, }) return () => { resizeObserver.disconnect() mutationObserver.disconnect() } }, []) return { ref: elementRef, isTruncated } } Practical Usage Here’s how to create a smart tooltip component using our hook: import { Tooltip, type TooltipProps } from '@your-ui-library' import { twMerge } from 'tailwind-merge' interface SmartTooltipProps extends React.HTMLAttributes { tooltipProps: Omit content: string } export const SmartTooltip = ({ tooltipProps, content, children, className, ...props }: SmartTooltipProps) => { const { isTruncated, ref } = useDetectedTruncation() return ( {children || content} ) } Performance Considerations Debounce Observations: For frequently resizing elements, consider debouncing the checks: const debouncedCheck = useDebounce(checkTruncation, 100) 2. Selective Observation: Only observe necessary attributes: resizeObserver.observe(element, { box: 'content-box' }) 3. Cleanup: Properly disconnect observers in the cleanup function to prevent memory leaks. Testing Strategies Verify the hook works in different scenarios: Static truncated text Dynamically loaded content Responsive layout changes Multi-line truncation (line-clamp) Nested scrolling containers describe('useDetectedTruncation', () => { it('detects horizontal truncation', () => { const { result } = renderHook(() => useDetectedTruncation()) render(
Long text that should truncate
, ) expect(result.current.isTruncated).toBe(true) }) it('ignores non-truncated content', () => { const { result } = renderHook(() => useDetectedTruncation()) render(
Short text
, ) expect(result.current.isTruncated).toBe(false) }) }) Going forward To make it even sexier, we could consider adding the following features: https://medium.com/media/d37d1c060816395a578aba437cc12a89/href Conclusion The useDetectedTruncation hook provides a clean, reusable solution for a common UI challenge. By encapsulating the detection logic, we can: Create more accessible interfaces Build smarter components Reduce unnecessary tooltip clutter Make our UIs more responsive to content changes. Originally published at https://www.pawelkrystkiewicz.pl on November 20, 2024.
PK

Paweł Krystkiewicz

November 20, 2024

More articles +(7)

Great ideas, great results

#portfolio
More projects +(4)

Let's build together!

#contact

Like what you see here?
Share your ideas with us. We think big!