Trusted by
Our insights
#articlesLessons 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
Great ideas, great results
#portfolioContract Builder Platform for Specialty Insurance
Transforming a prototype into a scalable production architecture used by UK insurers.
Technologies
Enterprise Component Library Architecture
Component library architecture for enterprise construction management platform.
Technologies
Manufacturing Database Platform
Engineered a scalable technical database driving manufacturing and production efficiency.
Technologies