Readiant
A minimal Web Component wrapper for the Readiant reader that provides seamless integration with modern web applications. Supports local documents and remote sources for private document access.
Installation
npm install @readiant/readerQuick Start
HTML Usage
<!DOCTYPE html>
<html>
<head>
<title>My Document Viewer</title>
</head>
<body>
<!-- Import the web component -->
<script type="module">
import '@readiant/reader';
</script>
<!-- Use the component -->
<readiant-reader
document-id="your-document-id"
page="1">
</readiant-reader>
</body>
</html>JavaScript/TypeScript Usage
import '@readiant/reader';
// Programmatically create and configure
const viewer = document.createElement('readiant-reader');
viewer.setAttribute('document-id', 'your-document-id');
viewer.setAttribute('page', '1');
document.body.appendChild(viewer);Configuration
Basic Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
document-id |
string | required | The ID of the document to display |
page |
number | 1 |
Initial page to display |
url |
string | '' |
Base URL for document sources |
use-signed-urls |
boolean | false |
Enable S3 signed URL support |
orientation |
string | 'auto' |
Document orientation (portrait, landscape) |
disable |
string | '' |
Comma-separated features to disable |
concurrency-limit |
number | 6 |
Maximum concurrent file downloads (1-20) |
lang |
string | 'en' |
BCP 47 language tag — automatically loads the matching locale file; falls back to English if not found |
translations |
string (JSON) | '' |
JSON-encoded key overrides applied on top of the lang locale (see Localization) |
Visual Appearance
| Attribute | Type | Default | Description |
|---|---|---|---|
zoom-level |
number | 1 |
Zoom level (1-5) |
font |
string | 'default' |
Font family |
letter-spacing |
number | 0 |
Letter spacing adjustment |
line-height |
number | 1.2 |
Line height multiplier |
line-highlighter-width |
number | 30 |
Line highlighter band width in pixels |
word-spacing |
number | 0 |
Word spacing adjustment |
screen-mode-level |
number | 1 |
Theme mode (1=light, 2=sepia, 3=dark) |
color-blind-filter |
string | 'none' |
Color blind accessibility filter |
image-quality-level |
number | 4 |
Image quality setting (1-4) |
text-mode-level |
number | 0 |
Text enhancement level |
Audio Options
| Attribute | Type | Default | Description |
|---|---|---|---|
audio-highlighting-level |
number | 0 |
Audio highlighting intensity |
countdown-level |
number | 0 |
Audio countdown settings |
playback-rate |
number | 1.0 |
Audio playback speed |
read-stop-level |
number | 0 |
Audio read stop behavior |
subtitle-font-size |
number | 16 |
Subtitle text size |
subtitle-level |
number | 0 |
Subtitle display settings |
Advanced Configuration
<!-- Basic configuration -->
<readiant-reader
document-id="doc-123"
page="5"
zoom-level="2"
screen-mode-level="3"
disable="print,fullscreen">
</readiant-reader>
<!-- Advanced visual customization -->
<readiant-reader
document-id="doc-456"
font="Arial"
letter-spacing="1"
line-height="1.5"
word-spacing="2"
color-blind-filter="deuteranopia"
image-quality-level="4">
</readiant-reader>
<!-- Audio-enabled document -->
<readiant-reader
document-id="doc-789"
audio-highlighting-level="2"
playback-rate="1.2"
subtitle-level="1"
subtitle-font-size="18">
</readiant-reader>
<!-- Performance optimization for large documents -->
<readiant-reader
document-id="doc-large"
concurrency-limit="10">
</readiant-reader>Localization
Set the lang attribute to any supported language tag. The component automatically loads the matching locale file from the package. If the language is not available it falls back to English.
<readiant-reader document-id="doc-123" lang="nl"></readiant-reader>Overriding specific strings
Pass a translations JSON object to override individual keys on top of the loaded language file. You only need to supply the keys you want to change — everything else comes from the locale file (or English if the locale is missing).
<readiant-reader
document-id="doc-123"
lang="nl"
translations='{"chapters":"Inhoudsopgave"}'>
</readiant-reader>If lang is absent or "en", the baked-in English strings are used and no network request is made.
Available locales
lang value |
Language |
|---|---|
en (default) |
English — baked in, no request |
nl |
Dutch |
React example
import { Reader } from '@readiant/reader/jsx';
function DocumentViewer({ documentId, lang = 'en' }) {
return (
<Reader
document-id={documentId}
lang={lang}
/>
);
}If you need to override specific strings on top of the locale:
<Reader
document-id={documentId}
lang="nl"
translations={JSON.stringify({ chapters: 'Inhoudsopgave' })}
/>Document Sources
Local Documents (Default)
By default, documents are loaded from the local server using relative paths:
<readiant-reader document-id="local-doc"></readiant-reader>Remote Document Sources
Load documents from cloud storage or any HTTP/HTTPS server:
<!-- Amazon S3 -->
<readiant-reader
document-id="remote-doc"
url="https://mybucket.s3.amazonaws.com/docs/remote-doc/">
</readiant-reader>
<!-- Custom Server -->
<readiant-reader
document-id="remote-doc"
url="https://documents.myserver.com/docs/remote-doc/">
</readiant-reader>Secure S3 Signed URLs
When using signed URLs, the url attribute should point directly to the signed index.json URL. The document-id attribute is optional in this case.
<!-- Private S3 documents with signed URLs -->
<readiant-reader
url="https://mybucket.s3.amazonaws.com/docs/doc-123/index.json?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=..."
use-signed-urls="true">
</readiant-reader>
<!-- With explicit document ID -->
<readiant-reader
document-id="doc-123"
url="https://mybucket.s3.amazonaws.com/docs/doc-123/index.json?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=..."
use-signed-urls="true">
</readiant-reader>Note: When use-signed-urls="true":
- The
urlattribute must point to the complete signedindex.jsonURL (not a base path) - All file paths in the
index.jsonshould be complete signed URLs - The
document-idis optional and will be auto-generated if not provided
Document Index Structure
Each document requires an index.json file that describes the document structure and files.
Document Index (Standard)
{
"type": "pdf",
"pages": 25,
"availableAudio": {
"provider1": [1, 2, 3, 4, 5],
"provider2": [1, 3, 5]
},
"blueprints": [
{
"page": 1,
"blueprint": ["element1", "element2"],
"viewBox": [0, 0, 612, 792],
"rotation": 0
}
],
"chapters": [
{
"title": "Chapter 1",
"page": 1
},
{
"title": "Chapter 2",
"page": 10
}
],
"files": [
"elements/page1.json",
"textContent/1.json",
"images/image1_4.jpg",
"audio/provider1/page1.mp3"
],
"imageInfo": [
{
"id": "image1",
"transparent": false
}
],
"inverted": false,
"offset": 0,
"rtl": false,
"spread": false
}PDF Document Index (With Signed URLs)
{
"type": "pdf",
"pages": 25,
"availableAudio": {
"provider1": [1, 2, 3, 4, 5],
"provider2": [1, 3, 5]
},
"blueprints": [
{
"page": 1,
"blueprint": ["element1", "element2"],
"viewBox": [0, 0, 612, 792],
"rotation": 0
}
],
"chapters": [
{
"title": "Chapter 1",
"page": 1
}
],
"files": [
"https://mybucket.s3.amazonaws.com/docs/doc-123/elements/page1.json?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...",
"https://mybucket.s3.amazonaws.com/docs/doc-123/textContent/1.json?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...",
"https://mybucket.s3.amazonaws.com/docs/doc-123/images/image1_4.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...",
"https://mybucket.s3.amazonaws.com/docs/doc-123/audio/provider1/page1.mp3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=..."
],
"imageInfo": [
{
"id": "image1",
"transparent": false,
"signedUrl": "https://mybucket.s3.amazonaws.com/docs/doc-123/images/image1_4.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=..."
}
],
"inverted": false,
"offset": 0,
"rtl": false,
"spread": false
}Framework Integration
Using the Web Component Directly
import '@readiant/reader';
function DocumentViewer({ documentId, page = 1 }) {
return (
<readiant-reader
document-id={documentId}
page={page}
/>
);
}Available Props:
All web component attributes are available as props with full TypeScript support:
interface ReaderProps {
// Document configuration
'document-id'?: string;
page?: number | string;
url?: string;
'use-signed-urls'?: boolean | string;
orientation?: string;
disable?: string;
// Visual appearance
'zoom-level'?: number | string;
font?: string;
'letter-spacing'?: number | string;
'line-height'?: number | string;
'line-highlighter-width'?: number | string;
'word-spacing'?: number | string;
'screen-mode-level'?: number | string;
'color-blind-filter'?: string;
'image-quality-level'?: number | string;
'text-mode-level'?: number | string;
// Audio options
'audio-highlighting-level'?: number | string;
'countdown-level'?: number | string;
'playback-rate'?: number | string;
'read-stop-level'?: number | string;
'subtitle-font-size'?: number | string;
'subtitle-level'?: number | string;
// Event handlers
onDocumentLoaded?: (event: CustomEvent<{
documentId: string;
isReady: boolean;
}>) => void;
onPageChanged?: (event: CustomEvent<{
pages: number[];
direction?: string;
previous?: {
duration: number;
pages: { page: number; audio?: { playbackPercentage: number } }[];
};
}>) => void;
onZoomChanged?: (event: CustomEvent<{
zoom: number;
}>) => void;
onThemeChanged?: (event: CustomEvent<{
theme: number;
}>) => void;
onError?: (event: CustomEvent<{
message: string;
type: string;
}>) => void;
}React
For better TypeScript support and a more React-friendly API, use the Reader wrapper component:
import { Reader } from '@readiant/reader/jsx';
function DocumentViewer({ documentId, page = 1 }: { documentId: string; page?: number }) {
return (
<Reader
document-id={documentId}
page={page}
zoom-level={2}
screen-mode-level={3}
onDocumentLoaded={(e) => console.log('Document loaded:', e.detail)}
onPageChanged={(e) => console.log('Current pages:', e.detail.pages)}
onZoomChanged={(e) => console.log('Zoom level:', e.detail.zoom)}
onError={(e) => console.error('Error:', e.detail.message)}
/>
);
}Vue 3
<template>
<readiant-reader
:document-id="documentId"
:page="page"
/>
</template>
<script setup>
import '@readiant/reader';
defineProps({
documentId: String,
page: { type: Number, default: 1 }
});
</script>Angular
// app.module.ts
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import '@readiant/reader';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
// ... other config
})
export class AppModule {}<!-- component.html -->
<readiant-reader
[attr.document-id]="documentId"
[attr.page]="page">
</readiant-reader>Browser Support
- Chrome/Edge 54+
- Firefox 63+
- Safari 10.1+
- iOS Safari 10.3+
- Android Chrome 54+
For older browsers, include the Web Components polyfill:
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@^2/webcomponents-bundle.js"></script>API
Properties
const viewer = document.querySelector('readiant-reader');
// Get current page
console.log(viewer.currentPage);
// Get total pages
console.log(viewer.totalPages);
// Check if document is loaded
console.log(viewer.isLoaded);Methods
const viewer = document.querySelector('readiant-reader');
// Navigation
viewer.goToPage(10);
viewer.nextPage();
viewer.previousPage();
// Zoom control
viewer.zoomIn();
viewer.zoomOut();
viewer.setZoom(1.5);
// Theme control
viewer.setTheme('dark'); // 'light' | 'sepia' | 'dark'
// Feature control
await viewer.toggleFullscreen();
viewer.print();
// Audio control
viewer.pauseAudio(); // pause audio playbackEvents
const viewer = document.querySelector('readiant-reader');
// Document loaded
viewer.addEventListener('document-loaded', (event) => {
console.log('Document loaded:', event.detail);
// event.detail: { documentId?: string, isReady: boolean }
});
// Page changed — fired for all navigation: UI buttons, swipe, keyboard, goToPage(), nextPage(), previousPage()
viewer.addEventListener('page-changed', (event) => {
console.log('Current pages:', event.detail.pages);
// event.detail: { pages: number[], direction?, previous? }
// pages: all currently visible page numbers (1 in portrait, up to 2 in landscape)
// direction: 'next' | 'previous' (omitted for direct jumps and initial load)
// previous: { duration, pages: [{ page, audio?: { playbackPercentage } }] } (omitted on initial load)
});
// Page closed (before unload)
viewer.addEventListener('page-closed', (event) => {
console.log('Page closed:', event.detail.pages);
// event.detail: { pages: number[] }
});
// Zoom changed
viewer.addEventListener('zoom-changed', (event) => {
console.log('Zoom level:', event.detail.zoom);
// event.detail: { zoom: number }
});
// Theme changed
viewer.addEventListener('theme-changed', (event) => {
console.log('Theme changed:', event.detail.theme);
// event.detail: { theme: number }
});
// Font changed
viewer.addEventListener('font-changed', (event) => {
console.log('Font changed:', event.detail.font);
// event.detail: { font?: string }
});
// Font size changed
viewer.addEventListener('font-size-changed', (event) => {
console.log('Font size:', event.detail.fontSize);
// event.detail: { fontSize: number }
});
// Letter spacing changed
viewer.addEventListener('letter-spacing-changed', (event) => {
console.log('Letter spacing:', event.detail.letterSpacing);
// event.detail: { letterSpacing: number }
});
// Line height changed
viewer.addEventListener('line-height-changed', (event) => {
console.log('Line height:', event.detail.lineHeight);
// event.detail: { lineHeight: number }
});
// Word spacing changed
viewer.addEventListener('word-spacing-changed', (event) => {
console.log('Word spacing:', event.detail.wordSpacing);
// event.detail: { wordSpacing: number }
});
// Image quality changed
viewer.addEventListener('image-quality-changed', (event) => {
console.log('Image quality level:', event.detail.imageQualityLevel);
// event.detail: { imageQualityLevel: number, imageQuality: string }
});
// Color blind filter changed
viewer.addEventListener('color-blind-filter-changed', (event) => {
console.log('Color blind filter:', event.detail.filter);
// event.detail: { filter?: string }
});
// Text mode changed
viewer.addEventListener('text-mode-changed', (event) => {
console.log('Text mode level:', event.detail.textModeLevel);
// event.detail: { textModeLevel: number }
});
// Audio play/pause
viewer.addEventListener('audio-play', (event) => {
console.log('Audio started playing');
// event.detail: { isPlaying: true, action: 'play' }
});
viewer.addEventListener('audio-pause', (event) => {
console.log('Audio paused');
// event.detail: { isPlaying: false, action: 'pause' }
});
// Audio highlighting changed
viewer.addEventListener('audio-highlighting-changed', (event) => {
console.log('Audio highlighting level:', event.detail.audioHighlightingLevel);
// event.detail: { audioHighlightingLevel: number }
});
// Countdown changed
viewer.addEventListener('countdown-changed', (event) => {
console.log('Countdown level:', event.detail.countdownLevel);
// event.detail: { countdownLevel: number }
});
// Playback rate changed
viewer.addEventListener('playback-rate-changed', (event) => {
console.log('Playback rate:', event.detail.playbackRate);
// event.detail: { playbackRate: number }
});
// Read stop changed
viewer.addEventListener('read-stop-changed', (event) => {
console.log('Read stop level:', event.detail.readStopLevel);
// event.detail: { readStopLevel: number }
});
// Subtitle changed
viewer.addEventListener('subtitle-changed', (event) => {
console.log('Subtitle level:', event.detail.subtitleLevel);
// event.detail: { subtitleLevel: number }
});
// Subtitle font size changed
viewer.addEventListener('subtitle-font-size-changed', (event) => {
console.log('Subtitle font size:', event.detail.subtitleFontSize);
// event.detail: { subtitleFontSize: number }
});
// Fullscreen changed
viewer.addEventListener('fullscreen-changed', (event) => {
console.log('Fullscreen:', event.detail.isFullscreen);
// event.detail: { isFullscreen: boolean }
});
// Orientation changed
viewer.addEventListener('orientation-changed', (event) => {
console.log('Orientation changed');
// event.detail: { action: 'toggle', orientation: string }
});
// Print
viewer.addEventListener('print', (event) => {
console.log('Print triggered:', event.detail.pages);
// event.detail: { pages: number[] }
});
// Resize
viewer.addEventListener('resize', (event) => {
console.log('Resize:', event.detail.width, event.detail.height);
// event.detail: { width: number, height: number }
});
// Translation
viewer.addEventListener('translate', (event) => {
console.log('Translate:', event.detail.language, event.detail.text);
// event.detail: { language: string, text: string }
});
// Audio management
viewer.addEventListener('audio-added', (event) => {
console.log('Audio added:', event.detail);
// event.detail: { page: number, provider: string, language?: string, voiceId?: string }
});
viewer.addEventListener('audio-switched', (event) => {
console.log('Audio switched:', event.detail.key);
// event.detail: { key: string }
});
// Annotations
viewer.addEventListener('annotation-added', (event) => {
console.log('Annotation saved:', event.detail.page);
// event.detail: { annotations: string, page: number }
});
// Issue reported
viewer.addEventListener('issue-reported', (event) => {
console.log('Issue reported:', event.detail);
// event.detail: { documentType: 'ePub' | 'PDF', issueType: 'audio' | 'content' | 'visual', pageOrChapter: number }
});
// Highlighting
viewer.addEventListener('highlighting-started', (event) => {
console.log('Highlighting started:', event.detail.indices);
// event.detail: { indices: number[] }
});
viewer.addEventListener('highlighting-stopped', (event) => {
console.log('Highlighting stopped');
// event.detail: { action: 'stop' }
});
// Error
viewer.addEventListener('error', (event) => {
console.error('Error:', event.detail.message);
// event.detail: { message: string, type: string }
});Document Structure
All document sources must maintain this folder structure:
baseUrl/ (or local server)
└── docs/
└── {id}/
├── index.json
├── audio/
│ ├── provider1/
│ │ ├── page1.mp3
│ │ ├── page1.json
│ │ └── ...
│ └── provider2/
├── elements/
│ ├── page1.json
│ ├── page2.json
│ └── ...
├── textContent/
│ ├── 1.json
│ ├── 2.json
│ └── ...
├── images/
│ ├── image1_4.png
│ ├── image2_4.jpg
│ └── ...
└── definitions/
└── definitions.json
Styling
The component uses Shadow DOM for style encapsulation. All visual customization is done via CSS custom properties set on the readiant-reader element (or any ancestor, including :root).
readiant-reader {
/* Size the component */
width: 100%;
height: 600px;
display: block;
/* Customize */
--readiant-font-family: 'Inter', sans-serif;
--readiant-button-height: 48px;
}CSS Custom Properties Reference
Component
| Property | Default | Description |
|---|---|---|
--readiant-component-width |
100% |
Component width |
--readiant-component-height |
100% |
Component height |
--readiant-font-family |
roboto, sans-serif |
Base font family |
Default Theme
| Property | Default | Description |
|---|---|---|
--readiant-default-background-color |
#fcfcfc |
Page/content background |
--readiant-default-menu-background-color |
#ececec |
Menu bar background |
--readiant-default-text-color |
#000 |
Body text color |
--readiant-default-active-background-color |
rgba(94, 111, 219, 6%) |
Active element background |
--readiant-default-active-border-color |
#5ba1d2 |
Active element border |
--readiant-default-hover-background-color |
rgba(94, 111, 219, 6%) |
Hover background |
--readiant-default-hover-border-color |
#5ba1d2 |
Hover border |
--readiant-default-button-background-color |
transparent |
Button background |
--readiant-default-button-box-shadow |
none |
Button box shadow |
--readiant-default-button-color |
#555 |
Button icon/text color |
Sepia Theme
| Property | Default | Description |
|---|---|---|
--readiant-sepia-background-color |
#eee2cd |
Page/content background |
--readiant-sepia-menu-background-color |
#f3f0d7 |
Menu bar background |
--readiant-sepia-text-color |
#5e454b |
Body text color |
--readiant-sepia-active-background-color |
rgba(94, 111, 219, 6%) |
Active element background |
--readiant-sepia-active-border-color |
#5ba1d2 |
Active element border |
--readiant-sepia-hover-background-color |
rgba(94, 111, 219, 6%) |
Hover background |
--readiant-sepia-hover-border-color |
#5ba1d2 |
Hover border |
--readiant-sepia-button-background-color |
transparent |
Button background |
--readiant-sepia-button-box-shadow |
none |
Button box shadow |
--readiant-sepia-button-color |
#555 |
Button icon/text color |
Dark Theme
| Property | Default | Description |
|---|---|---|
--readiant-dark-background-color |
#292929 |
Page/content background |
--readiant-dark-menu-background-color |
#121212 |
Menu bar background |
--readiant-dark-text-color |
#fff |
Body text color |
--readiant-dark-active-background-color |
rgba(94, 111, 219, 6%) |
Active element background |
--readiant-dark-active-border-color |
#5ba1d2 |
Active element border |
--readiant-dark-hover-background-color |
rgba(94, 111, 219, 6%) |
Hover background |
--readiant-dark-hover-border-color |
#5ba1d2 |
Hover border |
--readiant-dark-button-background-color |
transparent |
Button background |
--readiant-dark-button-box-shadow |
none |
Button box shadow |
--readiant-dark-button-color |
#555 |
Button icon/text color |
Buttons
| Property | Default | Description |
|---|---|---|
--readiant-button-height |
52px |
Height of toolbar buttons |
--readiant-button-width |
auto |
Width of toolbar buttons |
--readiant-button-padding |
4px 12px 0 |
Padding inside buttons |
--readiant-button-border-top |
0 |
Top border |
--readiant-button-border-right |
0 |
Right border |
--readiant-button-border-bottom |
4px solid transparent |
Bottom border (used for active indicator) |
--readiant-button-border-left |
0 |
Left border |
--readiant-button-border-radius |
0 |
Border radius |
Menu Bar
| Property | Default | Description |
|---|---|---|
--readiant-menu-height |
56px |
Total menu bar height (nav + progress bar) |
--readiant-menu-width |
100% |
Menu bar width |
--readiant-menu-top |
4px |
Distance from top (accounts for progress bar) |
--readiant-menu-bottom |
auto |
Distance from bottom |
--readiant-menu-left |
0 |
Distance from left |
--readiant-menu-right |
auto |
Distance from right |
--readiant-menu-buttons-width |
auto |
Width of the buttons container |
--readiant-menu-buttons-justify |
flex-end |
justify-content for the buttons row |
Progress Bar
| Property | Default | Description |
|---|---|---|
--readiant-progress-height |
4px |
Progress bar height |
--readiant-progress-width |
100% |
Progress bar width |
--readiant-progress-background-color |
#ececec |
Progress bar track color |
Navigation Buttons (prev/next page)
| Property | Default | Description |
|---|---|---|
--readiant-navigation-button-width |
4rem |
Button width |
--readiant-navigation-button-height |
4rem |
Button height |
--readiant-navigation-button-padding |
0 |
Button padding |
--readiant-navigation-button-border |
0 |
Button border |
--readiant-navigation-button-background-color |
transparent |
Button background |
--readiant-navigation-button-color |
rgba(85, 85, 85, 40%) |
Arrow color |
--readiant-navigation-button-hover-background-color |
transparent |
Hover background |
--readiant-navigation-button-hover-color |
#555 |
Hover arrow color |
Viewport
| Property | Default | Description |
|---|---|---|
--readiant-viewport-margin |
calc(56px + 2rem) 2rem 2rem |
Margin around the document viewport. Top accounts for the menu bar height. |
Element Visibility
Set any of these to none to hide the element, or inline-block/block/flex to show it.
| Property | Default | Element |
|---|---|---|
--readiant-logo-display |
block |
Logo in menu bar |
--readiant-annotations-button-display |
inline-block |
Annotations toolbar button |
--readiant-audio-progress-display |
block |
Audio progress bar |
--readiant-audio-progress-top-display |
inline-block |
Audio progress bar (top variant) |
--readiant-bottom-bar-display |
flex |
Bottom bar |
--readiant-bottom-bar-settings-button-display |
inline-block |
Bottom bar settings button |
--readiant-chapters-button-display |
inline-block |
Chapters toolbar button |
--readiant-first-page-display |
inline-block |
First page button |
--readiant-fullscreen-toggle-display |
inline-block |
Fullscreen toggle button |
--readiant-line-highlight-display |
inline-block |
Line highlighter button |
--readiant-mute-display |
inline-block |
Mute button |
--readiant-pause-display |
none |
Pause button (hidden by default; use with --readiant-play-display) |
--readiant-page-number-display |
inline-block |
Page number widget |
--readiant-play-display |
none |
Play button (hidden by default; use with --readiant-pause-display) |
--readiant-page-number-text-display |
none |
Text label alongside page number |
--readiant-playback-rate-toggle-display |
inline-block |
Playback rate toggle button |
--readiant-playback-rate-top-parent-display |
inline-block |
Playback rate container (top bar) |
--readiant-print-button-display |
inline-block |
Print button |
--readiant-screen-settings-button-display |
inline-block |
Screen settings button |
--readiant-search-button-display |
inline-block |
Search button |
--readiant-settings-button-display |
inline-block |
Settings button |
--readiant-start-display |
inline-block |
Start button (visible when audio is available; set to none to use play/pause mode) |
--readiant-stop-display |
inline-block |
Stop button (visible while audio is playing; set to none to use play/pause mode) |
--readiant-tooltip-display |
block |
Tooltips |
--readiant-unmute-display |
inline-block |
Unmute button |
Example — hide buttons you don't need:
readiant-reader {
--readiant-print-button-display: none;
--readiant-fullscreen-toggle-display: none;
--readiant-annotations-button-display: none;
}Icon Customization
Icons are rendered as <span> elements styled with CSS mask-image. Each icon has a corresponding CSS custom property that accepts any valid url() value — an inline SVG data URL, an external SVG file, or an SVG sprite fragment.
The built-in icon is always the fallback, so you only need to set the properties for icons you want to replace.
Replace a single icon
readiant-reader {
/* Inline SVG data URL */
--readiant-icon-settings: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='...'/></svg>");
/* External SVG file */
--readiant-icon-search: url('/assets/icons/my-search.svg');
/* SVG sprite fragment */
--readiant-icon-print: url('/assets/sprite.svg#print');
}Note: Icons inherit their color from the surrounding text via
background-color: currentColor+mask-image. You do not need to addfillorcolorto your SVG — the shape is used as a mask only.
All available icon properties
| Property | Icon |
|---|---|
--readiant-icon-annotations |
Annotations |
--readiant-icon-audio |
Audio / headphones |
--readiant-icon-audiohighlighterall |
Highlight all words |
--readiant-icon-audiohighlighterhide |
Hide audio highlights |
--readiant-icon-audiohighlightersentence |
Highlight by sentence |
--readiant-icon-backward |
Skip backward |
--readiant-icon-bottombarsettings |
Bottom bar settings |
--readiant-icon-chapters |
Chapters / table of contents |
--readiant-icon-close |
Close / dismiss |
--readiant-icon-closelinehighlighter |
Close line highlighter |
--readiant-icon-comments |
Comments |
--readiant-icon-countdowninstant |
Countdown (instant) |
--readiant-icon-countdownmanual |
Countdown (manual) |
--readiant-icon-countdownnormal |
Countdown (normal) |
--readiant-icon-eraser |
Eraser / clear annotations |
--readiant-icon-firstpage |
Go to first page |
--readiant-icon-forward |
Skip forward |
--readiant-icon-fullscreen |
Fullscreen (generic) |
--readiant-icon-fullscreenclose |
Exit fullscreen |
--readiant-icon-fullscreenopen |
Enter fullscreen |
--readiant-icon-grayscale |
Grayscale filter |
--readiant-icon-imagequalityauto |
Image quality: auto |
--readiant-icon-imagequalityhigh |
Image quality: high |
--readiant-icon-imagequalitylow |
Image quality: low |
--readiant-icon-issueaudio |
Report audio issue |
--readiant-icon-issuecontent |
Report content issue |
--readiant-icon-issuevisual |
Report visual issue |
--readiant-icon-lineheightlarge |
Line height: large |
--readiant-icon-lineheightnormal |
Line height: normal |
--readiant-icon-lineheightsmall |
Line height: small |
--readiant-icon-linehighlighter |
Line highlighter |
--readiant-icon-logo |
Brand logo |
--readiant-icon-marker |
Marker / highlight tool |
--readiant-icon-more |
More / overflow menu |
--readiant-icon-mute |
Mute audio |
--readiant-icon-next |
Next page |
--readiant-icon-orientation |
Orientation (generic) |
--readiant-icon-orientationlandscape |
Landscape orientation |
--readiant-icon-orientationportrait |
Portrait orientation |
--readiant-icon-pause |
Pause |
--readiant-icon-play |
Play |
--readiant-icon-playpause |
Play/pause toggle |
--readiant-icon-previous |
Previous page |
--readiant-icon-print |
|
--readiant-icon-readstoppage |
Read stop: per page |
--readiant-icon-readstopsentence |
Read stop: per sentence |
--readiant-icon-readstopword |
Read stop: per word |
--readiant-icon-screenmodedark |
Dark screen mode |
--readiant-icon-screenmodesepia |
Sepia screen mode |
--readiant-icon-screenmodenormal |
Normal screen mode |
--readiant-icon-screensettings |
Screen/display settings |
--readiant-icon-search |
Search |
--readiant-icon-settings |
Settings |
--readiant-icon-show |
Show / reveal |
--readiant-icon-smallannotation |
Small: annotation |
--readiant-icon-smallaudiolinehighlighter |
Small: audio line highlighter |
--readiant-icon-smallcolorblind |
Small: color blind filter |
--readiant-icon-smallcomments |
Small: comments |
--readiant-icon-smallcountdown |
Small: countdown |
--readiant-icon-smallfont |
Small: font |
--readiant-icon-smallfontsize |
Small: font size |
--readiant-icon-smallfullscreen |
Small: fullscreen |
--readiant-icon-smallimagequality |
Small: image quality |
--readiant-icon-smallissue |
Small: issue report |
--readiant-icon-smallletterspacing |
Small: letter spacing |
--readiant-icon-smalllineheight |
Small: line height |
--readiant-icon-smalllinehighlighter |
Small: line highlighter |
--readiant-icon-smallmarkings |
Small: markings |
--readiant-icon-smallorientation |
Small: orientation |
--readiant-icon-smallplaybackrate |
Small: playback rate |
--readiant-icon-smallreadstop |
Small: read stop |
--readiant-icon-smallscreenmode |
Small: screen mode |
--readiant-icon-smallsubtitles |
Small: subtitles |
--readiant-icon-smallsubtitlesfontsize |
Small: subtitles font size |
--readiant-icon-smalltextmode |
Small: text mode |
--readiant-icon-smalltranslate |
Small: translate |
--readiant-icon-smallvoice |
Small: voice |
--readiant-icon-smallwordspacing |
Small: word spacing |
--readiant-icon-smallzoom |
Small: zoom |
--readiant-icon-stop |
Stop |
--readiant-icon-subtitles |
Subtitles |
--readiant-icon-subtitlesall |
Subtitles: show all |
--readiant-icon-subtitleshide |
Subtitles: hide |
--readiant-icon-subtitlessentence |
Subtitles: by sentence |
--readiant-icon-textmodenormal |
Text mode: normal |
--readiant-icon-textmodeplaintext |
Text mode: plain text |
--readiant-icon-textmodesplit |
Text mode: split |
--readiant-icon-textmodeswitch |
Text mode: switch |
--readiant-icon-translate |
Translate |
--readiant-icon-undo |
Undo |
--readiant-icon-unmute |
Unmute audio |
License
see LICENSE file for details.
Support
- Documentation: https://readiant.app/documentation
- Issues: https://github.com/readiant/reader/issues
- Email: support@readiant.com