Umaki-chan - Official Character
Umaki is your one-stop solution for web development utilities, bringing together a carefully curated collection of powerful tools in a single, easy-to-use package. Crafted from real-world development experience, these utilities solve common challenges and streamline your workflow. Have a utility in mind that would make your development easier? Feel free to open an issue—I'm actively expanding this collection!
👨💻 Related Projects
| Project | Description | Use Case |
|---|---|---|
| piiiQcy | WordPress theme development boilerplate | WordPress site development |
| Acty | Static site boilerplate | Static HTML/CSS site creation |
| Quicint | EJS-based static HTML5 boilerplate | Mass-production static pages |
| vnl | Vite-based npm library development boilerplate | npm package development |
| umaki | Web development utility library | Common utility functions |
import { foo } from "umaki";A function to set or update global configuration options for the umaki library.
import { setConfig } from "umaki";
// Set a custom breakpoint value
setConfig({ BREAKPOINT: 1024 });
// You can also add custom configuration properties
setConfig({ customValue: "example" });A function to retrieve the entire current configuration object.
import { getConfig } from "umaki";
const config = getConfig();
console.log(config.BREAKPOINT); // e.g. 768 (default) or custom value if setA function to retrieve a specific configuration value by key.
import { getConfigValue } from "umaki";
const breakpoint = getConfigValue('BREAKPOINT');
console.log(breakpoint); // e.g. 768 (default) or custom value if setA function that executes a callback with the given value and returns the original value unchanged. Useful for performing side effects (logging, caching, analytics, etc.) without modifying the value.
Note: This function does not accept Promise values. For async operations, use tapAsync instead.
import { tap, removeAllHtmlTags } from "umaki";
const input = "<p>Hello <strong>World</strong>!</p>";
const output = tap(removeAllHtmlTags(input), (result) => {
console.log("Sanitized:", result);
});
// Logs: 'Sanitized: Hello World!'
// output = 'Hello World!'
// Caching example
const data = tap(processData(raw), (result) => {
cache.set("processed", result);
});
// Works with higher-order functions
const debouncedFn = tap(debounce(handler, 100), () => {
console.log("Debounced function created");
});A function that awaits a Promise, executes a callback with the resolved value, and returns the original value. Useful for performing side effects on async values without modifying them.
import { tapAsync } from "umaki";
// Logging async results
const data = await tapAsync(fetchData(), (result) => {
console.log("Fetched:", result);
});
// Caching async results
const getData = () =>
tapAsync(fetchFromServer(), (data) => cache.set("data", data));
// Parallel execution with individual completion tracking
await Promise.all([
tapAsync(fetchUserData(), (user) => console.log("user loaded")),
tapAsync(fetchProductData(), (products) => console.log("products loaded")),
tapAsync(fetchOrderData(), (orders) => console.log("orders loaded")),
]);
// Event emission on async completion
const loadUser = () =>
tapAsync(fetchUser(), (user) => eventEmitter.emit("userLoaded", user));A function that stops scrolling.
import { bgScrollStop } from "umaki";
bgScrollStop(); // scroll stop
bgScrollStop(false); // scroll startA function that copies text to the clipboard using the modern Clipboard API.
import { copyToClipboard } from "umaki";
// Basic usage
const success = await copyToClipboard("Hello, World!");
if (success) {
console.log("Copied to clipboard!");
} else {
console.log("Failed to copy");
}
// Use in click handler
button.addEventListener("click", async () => {
const copied = await copyToClipboard(textToCopy);
showToast(copied ? "Copied!" : "Copy failed");
});A function that prevents the default event behavior.
import { pd } from "umaki";
document.getElementById("myElement").addEventListener("click", pd);A function that scrolls to a specific hash position.
import { scrollToHash } from "umaki";
(async () => {
await scrollToHash("#target");
console.log("Scrolled!");
// smooth scroll
await scrollToHash("#target", true);
// smooth scroll + offset
await scrollToHash("#target", true, 100);
})();A function that controls the playback of a video element.
import { videoPlayControl } from "umaki";
const videoElement = document.getElementById("myVideo");
videoPlayControl(videoElement, true); // play
videoPlayControl(videoElement, false); // pause
// set currentTime
videoPlayControl(videoElement, true, 10); // play and set currentTime to 10A function that converts a date string to a specific format.
import { changeDateStringToSpecificFormat } from "umaki";
// Basic usage
const date = "2023-10-05";
const formattedDate = changeDateStringToSpecificFormat(date, "MM/DD/YYYY");
console.log(formattedDate); // '10/05/2023'
// With timezone
const dateWithTz = "2025-04-25T00:00:00.000Z";
const formattedDateWithTz = changeDateStringToSpecificFormat(
dateWithTz,
"YYYY-MM-DD HH:mm:ss",
"Asia/Tokyo"
);
console.log(formattedDateWithTz); // '2025-04-25 09:00:00'
// With different timezone
const formattedDateWithDifferentTz = changeDateStringToSpecificFormat(
dateWithTz,
"YYYY-MM-DD HH:mm:ss",
"America/New_York"
);
console.log(formattedDateWithDifferentTz); // '2025-04-24 20:00:00'Note: This function focuses on timezone handling rather than locale-specific formatting. When a timezone is specified, the date will be converted to that timezone before formatting.
A function that converts a JSON string to a JSON object.
import { jsonStringToJsonObject } from "umaki";
const jsonString = '{"name": "John", "age": 30}';
const jsonObject = jsonStringToJsonObject(jsonString);
console.log(jsonObject); // { name: 'John', age: 30 }A function that limits the number of times a function is called to at most one time over a specified time period.
import { debounce } from "umaki";
const debouncedFunction = debounce(() => {
console.log("Debounced!");
}, 300);
window.addEventListener("resize", debouncedFunction);A function that limits the number of times a function is called to a maximum of once in a specified period.
import { throttle } from "umaki";
const throttledFunction = throttle(() => {
console.log("Throttled!");
}, 300);
window.addEventListener("scroll", throttledFunction);A function that returns the aspect ratio of the specified width and height.
import { getAspectRatio } from "umaki";
const aspectRatio = getAspectRatio(1920, 1080);
console.log(aspectRatio); // { w: 16, h: 9 }A function that retrieves the class names of the specified HTML element as an array.
import { getClassNames } from "umaki";
const element = document.createElement("div");
element.className = "class1 class2 class3";
const classNames = getClassNames(element);
console.log(classNames); // ['class1', 'class2', 'class3']A function that retrieves the height of the document.
import { getDocumentHeight } from "umaki";
const height = getDocumentHeight();
console.log(height);A function that retrieves the event paths.
import { getEventPaths } from "umaki";
document.addEventListener("click", (event) => {
const paths = getEventPaths(event);
console.log(paths);
});A function that calculates the greatest common divisor of two numbers.
import { getGcd } from "umaki";
const gcd = getGcd(48, 18);
console.log(gcd); // 6A function that generates a Gravatar avatar image URL from an email address and size parameter.
import { getGravatarUrl } from "umaki";
const avatarUrl = getGravatarUrl('user@example.com', 80);
console.log(avatarUrl);
// 'https://www.gravatar.com/avatar/b58996c504c5638798eb6b511e6f49af?s=80&d=404'
// Different size
const largeAvatar = getGravatarUrl('user@example.com', 200);
// Email with uppercase and whitespace (automatically normalized)
const normalizedAvatar = getGravatarUrl(' USER@EXAMPLE.COM ', 80);
// Same result as above due to normalizationA function that retrieves the current orientation of the device.
import { getOrientation } from "umaki";
const orientation = getOrientation();
console.log(orientation); // 'landscape' or 'portrait'A function that recursively retrieves the parent elements of the specified HTML element.
import { getParentList } from "umaki";
const element = document.createElement("div");
const parentList = getParentList(element);
console.log(parentList);A function that retrieves the value of a specified query parameter from the URL.
import { getQueryParams } from "umaki";
// Basic usage
const param = getQueryParams("id"); // returns value from window.location.search
// With custom search string
const customParam = getQueryParams("id", { searchString: "?id=123" });
// With parse options
const paramWithOptions = getQueryParams("id", {
parseOptions: { arrayFormat: "bracket" },
});
// With both custom search string and parse options
const customParamWithOptions = getQueryParams("id", {
searchString: "?id=123",
parseOptions: { arrayFormat: "bracket" },
});A function that returns a random integer between min (inclusive) and max (inclusive).
import { getRandomInt } from "umaki";
// Basic usage
const random = getRandomInt(1, 10);
console.log(random); // Random integer from 1 to 10
// Use for array index
const items = ["apple", "banana", "orange"];
const randomItem = items[getRandomInt(0, items.length - 1)];
// Generate random delay
const delay = getRandomInt(100, 500);
await sleep(delay / 1000);A function that returns a human-readable relative time string (e.g., "3 minutes ago", "2 days ago") using the Intl.RelativeTimeFormat API.
import { getRelativeTime } from "umaki";
// Basic usage (Japanese locale by default)
const pastDate = new Date(Date.now() - 5 * 60 * 1000);
console.log(getRelativeTime(pastDate)); // "5 分前"
// English locale
console.log(getRelativeTime(pastDate, "en")); // "5 minutes ago"
// Future dates
const futureDate = new Date(Date.now() + 2 * 24 * 60 * 60 * 1000);
console.log(getRelativeTime(futureDate)); // "2 日後"
// With timestamp or ISO string
console.log(getRelativeTime(1702800000000, "en")); // relative to now
console.log(getRelativeTime("2024-01-15T12:00:00Z", "en"));A function that converts a pixel value to rem units.
import { getRem } from "umaki";
const remValue = getRem(16);
console.log(remValue); // '1rem'A function that retrieves the width of the scrollbar.
import { getScrollbarWidth } from "umaki";
const scrollbarWidth = getScrollbarWidth();
console.log(scrollbarWidth); // 15A function that retrieves a value from session storage.
import { getSessionStorage } from "umaki";
const value = getSessionStorage("testKey");
console.log(value); // 'testValue'A function that retrieves the length of a string (considering Unicode characters).
import { getStringLength } from "umaki";
const length = getStringLength("こんにちは");
console.log(length); // 5A function that retrieves the value of the specified CSS custom property.
import { getStylePropertyValue } from "umaki";
const value = getStylePropertyValue("--custom-property");
console.log(value);A function that retrieves the value of the specified CSS custom property as a number.
import { getStylePropertyValueToNumber } from "umaki";
const value = getStylePropertyValueToNumber("--custom-property");
console.log(value);A function that retrieves user agent information.
import { getUaData } from "umaki";
const uaData = getUaData();
console.log(uaData);
// {
// browserName: 'chrome',
// browserVersion: '91.0.4472.124',
// browserEngine: 'blink',
// osName: 'windows',
// type: 'desktop',
// touchSupport: false
// }A function that checks if the current date is after a specified date.
import { isAfterDateTime } from "umaki";
const targetDate = "2023-10-01";
const result = isAfterDateTime(targetDate);
console.log(result); // true or false
// With custom current date
import dayjs from "dayjs";
const customCurrentDate = "2023-11-15";
const resultWithCustomDate = isAfterDateTime(targetDate, dayjs(customCurrentDate));A function that checks if the current date is between two specified dates.
import { isBetweenDateTime } from "umaki";
const dateA = "2023-10-01";
const dateB = "2023-10-10";
const result = isBetweenDateTime(dateA, dateB);
console.log(result); // true or falseA function that checks if all elements exist.
import { isExistAllElements } from "umaki";
const elements = [
document.createElement("div"),
document.createElement("span"),
];
const result = isExistAllElements(elements);
console.log(result); // true or falseA function that checks if an element is currently visible within the viewport.
import { isInViewport } from "umaki";
const element = document.getElementById("target");
// Check if any part of the element is visible
const isVisible = isInViewport(element);
console.log(isVisible); // true or false
// Check if the entire element is visible
const isFullyVisible = isInViewport(element, { threshold: 1 });
// Check if at least 50% of the element is visible
const isHalfVisible = isInViewport(element, { threshold: 0.5 });
// With expanded viewport bounds (trigger 100px before element enters viewport)
const isNearViewport = isInViewport(element, { rootMargin: "100px" });A function that checks if the device is an iPad.
import { isIpad } from "umaki";
const result = isIpad();
console.log(result); // true or falseA function that checks if a specific key exists in an object.
import { isKeyExists } from "umaki";
const obj = { a: 1, b: 2 };
const result = isKeyExists(obj, "a");
console.log(result); // true or falseA function that checks if the browser is currently online using the Navigator.onLine API.
import { isOnline } from "umaki";
// Basic usage
if (isOnline()) {
// Perform network request
fetchData();
} else {
// Show offline message
showOfflineMessage();
}
// Use with event listeners for online/offline detection
window.addEventListener("online", () => {
if (isOnline()) {
console.log("Back online!");
}
});A function that checks if the browser is Safari.
import { isSafari } from "umaki";
const result = isSafari();
console.log(result); // true or falseA function that checks if an element is scrollable.
import { isScrollable } from "umaki";
const element = document.createElement("div");
element.style.overflow = "auto";
element.innerHTML = '<div style="height: 200px;"></div>';
const result = isScrollable(element);
console.log(result); // true or falseA function that checks if the device supports touch.
import { isTouchSupport } from "umaki";
const result = isTouchSupport();
console.log(result); // true or falseA function that checks the current device size based on window width and the configured breakpoint.
import { checkDeviceSize, setConfig } from "umaki";
// Using default breakpoint (768px)
const deviceSize = checkDeviceSize();
console.log(deviceSize); // 'md' if window.innerWidth > 768, otherwise 'sm'
// Customize the breakpoint
setConfig({ BREAKPOINT: 1024 });
const newDeviceSize = checkDeviceSize();
console.log(newDeviceSize); // 'md' if window.innerWidth > 1024, otherwise 'sm'A function that removes all HTML tags from a string.
import { removeAllHtmlTags } from "umaki";
const input = "<p>Hello <strong>World</strong>!</p>";
const output = removeAllHtmlTags(input);
console.log(output); // 'Hello World!'A function that removes an attribute from the specified HTML element.
import { removeAttribute } from "umaki";
const element = document.createElement("div");
element.setAttribute("data-test", "value");
removeAttribute(element, "data-test");
console.log(element.hasAttribute("data-test")); // falseA function that removes an item with the specified key from session storage.
import { removeSessionStorage } from "umaki";
const key = "testKey";
removeSessionStorage(key);
console.log(sessionStorage.getItem(key)); // nullA function that removes the specified CSS custom property.
import { removeStylePropertyValue } from "umaki";
const key = "--custom-property";
removeStylePropertyValue(key);
console.log(getComputedStyle(document.documentElement).getPropertyValue(key)); // ''A function that sets a CSS variable to 100vh to address viewport unit issues on mobile devices.
import { set100vh } from "umaki";
set100vh();A function that sets a CSS variable to 100vw minus the scrollbar width.
import { set100vw } from "umaki";
set100vw();A function that sets an attribute on the specified HTML element.
import { setAttribute } from "umaki";
const element = document.createElement("div");
setAttribute(element, "data-test", "value");
console.log(element.getAttribute("data-test")); // 'value'A function that adjusts the horizontal scroll position of the root element to center the target element.
import { setScrollPositionToCenter } from "umaki";
const rootElement = document.getElementById("root");
const targetElement = document.getElementById("target");
setScrollPositionToCenter(rootElement, targetElement);A function that sets a value in session storage.
import { setSessionStorage } from "umaki";
const key = "testKey";
const value = "testValue";
setSessionStorage(key, value);
console.log(sessionStorage.getItem(key)); // 'testValue'A function that sets a CSS custom property on the root element.
import { setStylePropertyValue } from "umaki";
const key = "--custom-color";
const value = "blue";
setStylePropertyValue(key, value);
console.log(getComputedStyle(document.documentElement).getPropertyValue(key)); // 'blue'A function that converts a string to a boolean value.
import { toBoolean } from "umaki";
console.log(toBoolean("true")); // true
console.log(toBoolean("false")); // false
console.log(toBoolean("random")); // falseA function that converts a number to a positive number. Returns the absolute value if the number is negative.
import { toPositiveNumber } from "umaki";
console.log(toPositiveNumber(5)); // 5
console.log(toPositiveNumber(-5)); // 5
console.log(toPositiveNumber(0)); // 0
console.log(toPositiveNumber(-3.14)); // 3.14
console.log(toPositiveNumber(3.14)); // 3.14A function that wraps each character of the text content of an HTML element with individual <span> elements.
import { wrapTextWithSpans } from "umaki";
const element = document.createElement("div");
element.textContent = "hello";
wrapTextWithSpans(element);
console.log(element.innerHTML); // '<span>h</span><span>e</span><span>l</span><span>l</span><span>o</span>'A function that pauses execution for a specified amount of time.
import { sleep } from "umaki";
(async () => {
console.log("Start");
await sleep(1); // 1s wait
console.log("End");
})();A function that waits until all images and videos in the document are fully loaded.
import { waitForAllMediaLoaded } from "umaki";
(async () => {
const allMediaLoaded = await waitForAllMediaLoaded();
console.log(allMediaLoaded); // true or false
// first view only
const firstViewMediaLoaded = await waitForAllMediaLoaded(true);
console.log(firstViewMediaLoaded); // true or false
})();A function that sanitizes HTML strings to prevent XSS attacks.
import { sanitizeHtml } from "umaki";
// Basic usage
const sanitized = sanitizeHtml(
'<script>alert("xss")</script><p>Hello World</p>'
);
console.log(sanitized); // '<p>Hello World</p>'
// With custom config
const config = { ALLOWED_TAGS: ["p"] };
const sanitizedWithConfig = sanitizeHtml("<p>Hello World</p>", config);
console.log(sanitizedWithConfig); // '<p>Hello World</p>'This function uses isomorphic-dompurify and has the following features:
- Removes dangerous HTML tags and attributes by default
- Allows flexible control through custom configuration
- Can be used on both server-side and client-side
This section provides tips and patterns for getting the most out of umaki utilities.
The tap and tapAsync functions allow you to add callbacks to any umaki function without modifying its behavior. This is useful for logging, analytics, caching, and more.
| Function Type | Use | Example |
|---|---|---|
| Sync (returns value) | tap |
tap(removeAllHtmlTags(input), callback) |
| Sync (returns void) | tap |
tap(set100vh(), callback) |
| Async (returns Promise) | tapAsync |
tapAsync(sleep(1), callback) |
Tip
tap prevents Promise values at the type level. If you accidentally pass a Promise, TypeScript will show an error.
📝 Logging & Debugging
import { tap, removeAllHtmlTags, getUaData } from "umaki";
// Log sanitized HTML output
const clean = tap(removeAllHtmlTags(dirtyHtml), (result) => {
console.log("[DEBUG] Sanitized HTML:", result);
});
// Log user agent data
const ua = tap(getUaData(), (data) => {
console.log("[DEBUG] UA:", data.browserName, data.osName);
});📊 Analytics & Tracking
import { tap, tapAsync, getUaData, waitForAllMediaLoaded } from "umaki";
// Track device information
const uaData = tap(getUaData(), (data) => {
analytics.track("device_detected", {
browser: data.browserName,
os: data.osName,
type: data.type,
});
});
// Track media load completion
await tapAsync(waitForAllMediaLoaded(), (success) => {
analytics.track("media_loaded", { success, timestamp: Date.now() });
});💾 Caching
import { tap, tapAsync, getScrollbarWidth } from "umaki";
// Cache scrollbar width (useful for repeated access)
const scrollbarWidth = tap(getScrollbarWidth(), (width) => {
sessionStorage.setItem("scrollbarWidth", String(width));
});
// Cache async fetch results
const getData = () =>
tapAsync(fetchFromAPI(), (data) => {
localStorage.setItem("cachedData", JSON.stringify(data));
});🎯 Event Emission
import { tap, tapAsync, checkDeviceSize, waitForAllMediaLoaded } from "umaki";
// Emit event on device size check
const deviceSize = tap(checkDeviceSize(), (size) => {
window.dispatchEvent(new CustomEvent("deviceSizeChecked", { detail: size }));
});
// Emit event when all media is loaded
await tapAsync(waitForAllMediaLoaded(true), () => {
window.dispatchEvent(new CustomEvent("firstViewMediaReady"));
});⚡ Parallel Async Operations
import { tapAsync, sleep, waitForAllMediaLoaded } from "umaki";
// Track multiple async operations independently
const [_, mediaLoaded] = await Promise.all([
tapAsync(sleep(1), () => console.log("⏱️ 1 second elapsed")),
tapAsync(waitForAllMediaLoaded(), (ok) => console.log("🖼️ Media:", ok ? "ready" : "failed")),
]);Note
When using tap with functions that return void (like set100vh, bgScrollStop), the callback receives undefined. This is still useful for completion notifications.
import { tap, set100vh, bgScrollStop } from "umaki";
// Notification when viewport units are set
tap(set100vh(), () => {
console.log("✅ 100vh CSS variable has been set");
});
// Notification when scroll is stopped
tap(bgScrollStop(true), () => {
console.log("🔒 Background scroll locked");
});Specific usage for frameworks or various tools is described below.
Umaki supports subpath exports, allowing you to import only the modules you need. This is particularly useful for avoiding unnecessary dependencies and reducing bundle size.
// Import only the 'get' module (does not load isomorphic-dompurify)
import { getUaData, getScrollbarWidth } from "umaki/get";
// Import only the 'is' module
import { isIpad, isSafari } from "umaki/is";
// Import only the 'security' module (loads isomorphic-dompurify)
import { sanitizeHtml } from "umaki/security";
// Traditional import (loads all modules)
import { getUaData, sanitizeHtml } from "umaki";| Path | Description |
|---|---|
umaki/callback |
Callback utilities (tap, tapAsync) |
umaki/config |
Configuration (setConfig, getConfig) |
umaki/control |
Scroll control, video playback, etc. |
umaki/convert |
Data conversion (dates, JSON) |
umaki/eventControl |
debounce, throttle |
umaki/get |
Value retrieval (DOM, UA, etc.) |
umaki/is |
Boolean checks (device detection, etc.) |
umaki/remove |
DOM/storage removal utilities |
umaki/security |
HTML sanitization |
umaki/set |
DOM/storage setters |
umaki/to |
Type conversions |
umaki/transform |
DOM transformations |
umaki/wait |
Async utilities (sleep, media loading) |
When using with Astro for SSR, add umaki to vite.ssr.noExternal.
// astro.config.ts
export default defineConfig(({ mode }) => {
return {
vite: {
ssr: {
noExternal: ["umaki"],
},
},
};
});When using umaki with Next.js, you may encounter ESM compatibility issues during SSR if you import from the main entry point. This is because isomorphic-dompurify (used by the security module) has dependencies that can cause issues in SSR environments.
Recommended: Use subpath exports to import only the modules you need:
// ✅ Safe for Next.js SSR (does not load isomorphic-dompurify)
import { getUaData } from "umaki/get";
import { debounce } from "umaki/eventControl";
// ⚠️ Only import security module if you need HTML sanitization
import { sanitizeHtml } from "umaki/security";This approach avoids loading isomorphic-dompurify and its dependencies unless you specifically need the sanitizeHtml function.