# ReadlyGo (MVP)

ReadlyGo is a standalone JavaScript accessibility widget designed to run on any HTML site.

Created and maintained by Caleidoscopio Web Agency.

This MVP is frontend-only and zero-backend.

## Scope of this MVP

Implemented in v1:
- Visual and readability controls (contrast modes, font tweaks, spacing, alignment, highlights)
- Orientation controls (hide images/emoji, reading mask, reading guide, cursor size)
- Multilanguage UI (English/Italian) with flag switcher in the panel header
- Popup UI + trigger button + local persistence
- Public API + headless event compatibility (`readabler-headless`)

## Install and build

```bash
npm install
npm run build
```

Output files:
- `dist/readlygo.esm.js`
- `dist/readlygo.umd.js`

## CDN (Ready To Go)

Recommended free stack:
- Load it from jsDelivr in your projects

CDN URLs after publish:
- UMD latest (auto-update): `https://cdn.jsdelivr.net/npm/@caleidoscopioweb/readlygo@latest/dist/readlygo.umd.js`
- UMD (pin version): `https://cdn.jsdelivr.net/npm/@caleidoscopioweb/readlygo@0.1.0/dist/readlygo.umd.js` (Check Version)
- ESM (pin version): `https://cdn.jsdelivr.net/npm/@caleidoscopioweb/readlygo@0.1.0/dist/readlygo.esm.js` (Check Version)

Use pinned versions in production to avoid unexpected breaking changes.

## CDN snippet (UMD)

```html
<script>
  window.__READLYGO_CONFIG__ = {
    showButton: true,
    defaultLanguage: "en",
    rememberLanguage: true,
    remember: true,
    styleNonce: "",
    assetsBaseUrl: "https://cdn.jsdelivr.net/npm/@caleidoscopioweb/readlygo@latest/assets",
    languageSelector: {
      enabled: true,
      size: 18,
      showCodes: false
    },
    localization: {
      defaultLanguage: "en",
      languages: ["en", "it"],
      flags: {
        en: "https://cdn.jsdelivr.net/npm/@caleidoscopioweb/readlygo@latest/assets/flags/gb.svg",
        it: "https://cdn.jsdelivr.net/npm/@caleidoscopioweb/readlygo@latest/assets/flags/it.svg"
      }
    },
    floatingButton: {
      enabled: true,
      position: "bottom-left",
      variant: "icon",
      iconUrl: "https://cdn.jsdelivr.net/npm/@caleidoscopioweb/readlygo@latest/assets/icons/accessibility-icon.svg",
      iconSize: 22,
      title: "Open accessibility options",
      background: "#b96cbd",
      textColor: "#ffffff",
      iconColor: "#ffffff",
      borderColor: "#a75eab",
      boxShadow: "0 10px 30px rgba(0, 16, 45, 0.28)",
      boxShadowHover: "0 0 0 3px rgba(185, 108, 189, 0.4)",
      padding: "0 10px",
      margin: 24,
      offsetX: 24,
      offsetY: 16
    },
    customTrigger: {
      enabled: true,
      selector: "readlygo-trigger,[data-readlygo-trigger]",
      variant: "icon-text",
      size: "md",
      label: "Accessibility"
    },
    branding: {
      enabled: true,
      text: "Created and maintained by Caleidoscopio Web Agency",
      url: ""
    }
  };
</script>
<script
  src="https://cdn.jsdelivr.net/npm/@caleidoscopioweb/readlygo@latest/dist/readlygo.umd.js"
  defer
  onload="window.ReadlyGo.init(window.__READLYGO_CONFIG__ || {})"
></script>
```

## ESM usage

```html
<script type="module">
  import ReadlyGo from "https://cdn.jsdelivr.net/npm/@caleidoscopioweb/readlygo@latest/dist/readlygo.esm.js";

  ReadlyGo.init({
    showButton: true,
    defaultLanguage: "it",
    rememberLanguage: true,
    languageSelector: {
      enabled: true
    },
    localization: {
      flags: {
        en: "/assets/flags/gb.svg",
        it: "/assets/flags/it.svg"
      }
    },
    floatingButton: {
      position: "bottom-left",
      variant: "icon",
      title: "Open accessibility options",
      background: "#b96cbd",
      textColor: "#ffffff",
      iconColor: "#ffffff",
      borderColor: "#a75eab",
      boxShadow: "0 10px 30px rgba(0, 16, 45, 0.28)",
      boxShadowHover: "0 0 0 3px rgba(185, 108, 189, 0.4)",
      padding: "0 10px",
      margin: 24,
      offsetX: 24,
      offsetY: 16
    },
    openHotkey: "alt+a"
  });
</script>
```

## Multilanguage (EN/IT)

- Built-in locales: English (`en`) and Italian (`it`)
- Language switcher is shown in the widget header (flag buttons)
- Change is applied live to title, subtitle, section names, controls, and footer actions
- Set the startup language from script with `defaultLanguage: "it"` (or `en`)
- If `rememberLanguage: true`, selected language is persisted

## Custom trigger tag (menu/div)

You can place custom triggers anywhere in HTML:

```html
<readlygo-trigger
  data-rg-variant="icon-text"
  data-rg-label="Accessibility"
  data-rg-size="md"
></readlygo-trigger>

<div
  data-readlygo-trigger
  data-rg-variant="icon"
  data-rg-size="sm"
  data-rg-background="#b96cbd"
  data-rg-text-color="#ffffff"
  data-rg-icon-color="#ffffff"
  data-rg-padding="0 10px"
  data-rg-title="Open accessibility options"
></div>
```

Supported per-tag options (`data-rg-*`):
- `variant`: `icon`, `text`, `icon-text`
- `size`: `sm`, `md`, `lg`
- `label`
- `icon-url`
- `icon-size`
- `background`
- `text-color` (or legacy `color`)
- `icon-color`
- `padding`
- `border-color`
- `box-shadow`
- `box-shadow-hover` (or `hover-box-shadow`)
- `title`
- `aria-label`
- `class-name`

## Public API

- `window.ReadlyGo.init(config)`
- `window.ReadlyGo.dispatch(actionOrPayload, value?)`
- `window.ReadlyGo.reset()`
- `window.ReadlyGo.destroy()`
- `window.ReadlyGo.getState()`
- `window.ReadlyGo.isReady()`
- `window.ReadlyGo.normalizeActionName(action)`

Examples:

```js
ReadlyGo.dispatch("highlightLinks");
ReadlyGo.dispatch({ action: "fontSizing", value: 2 });
ReadlyGo.reset();
```

## Headless event compatibility

Dispatch from external scripts:

```js
window.dispatchEvent(
  new CustomEvent("readabler-headless", {
    detail: { action: "highlight-links", value: true }
  })
);
```

Action names are normalized to camelCase, so kebab/snake prefixes are accepted (for example: `highlight-links`, `readlygo-action-highlight-links`, `mdp-action-highlight-links`).

Runtime events:
- `ReadlyGoActionChanged`
- `ReadlyGoLanguageChanged`

## Supported actions

Value actions:
- `contentScaling`
- `fontSizing`
- `lineHeight`
- `letterSpacing`

Toggle actions:
- `readableFont`
- `dyslexiaFont`
- `alignLeft`
- `alignCenter`
- `alignRight`
- `darkContrast`
- `lightContrast`
- `monochrome`
- `highSaturation`
- `highContrast`
- `lowSaturation`
- `highlightTitles`
- `highlightLinks`
- `highlightHover`
- `highlightFocus`
- `hideImages`
- `hideEmoji`
- `readingMask`
- `readingGuide`
- `bigBlackCursor`
- `bigWhiteCursor`

## Key config options

- `storageKey` (default: `readlygo`)
- `remember` (default: `true`)
- `showButton` (default: `true`)
- `startOpen` (default: `false`)
- `defaultLanguage` (`en` or `it`, default: `en`)
- `language` (`en` or `it`, legacy alias of `defaultLanguage`)
- `rememberLanguage` (default: `true`)
- `languageSelector` (`enabled`, `size`, `showCodes`)
- `localization` (`defaultLanguage`, `languages`, `flags`, `locales`)
- `buttonLabel` (default: `ReadlyGo`)
- `buttonPosition` (`bottom-left`, `bottom-right`, `top-right`, `top-left`)
- `buttonVariant` (`icon`, `text`, `icon-text`, default: `icon`)
- `buttonIconUrl` (default: built-in white accessibility icon)
- `buttonIconSize` (default: `22`)
- `buttonAriaLabel`
- `buttonTitle`
- `buttonBackground`
- `buttonTextColor`
- `buttonIconColor`
- `buttonBorderColor`
- `buttonBoxShadow`
- `buttonBoxShadowHover`
- `buttonPadding`
- `buttonMargin` (distance from viewport, px or CSS value)
- `buttonOffsetX` (horizontal floating offset; defaults to `buttonMargin` if not set)
- `buttonOffsetY` (vertical floating offset; defaults to `buttonMargin` if not set)
- `floatingButton` (`enabled`, `position`, `variant`, `iconUrl`, `iconSize`, `label`, `ariaLabel`, `title`, `background`, `textColor`, `iconColor`, `borderColor`, `boxShadow`, `boxShadowHover`, `padding`, `margin`, `offsetX`, `offsetY`, aliases `marginX`, `marginY`)
- `popupTitle` (default: `ReadlyGo`)
- `panelSubtitle` (default: `Accessibility Toolkit`)
- `closeOnOverlay`
- `openHotkey` (default: `alt+a`)
- `theme` (`triggerBg`, `triggerText`, `triggerIcon`, `triggerBorder`, `triggerShadow`, `triggerShadowHover`, `triggerPadding`, `triggerMargin`, `triggerOffsetX`, `triggerOffsetY`)
- `customTrigger` (`enabled`, `selector`, `variant`, `size`, `label`, `iconUrl`, `iconSize`, `background`, `textColor`, `iconColor`, `padding`, `borderColor`, `title`, `className`)
- `branding` (`enabled`, `text`, `url`, `target`, `rel`)
- `enable` (action visibility map)
- `initialState`
- `styleNonce` (for CSP nonce on injected `<style>` tags)
- `assetsBaseUrl` (for OpenDyslexic files in `font-dislessici/`; if omitted, the runtime tries auto-detection from the script URL)

## Browser fallback behavior

- If `localStorage` is unavailable, state falls back to in-memory runtime storage.

## Notes for SPA and dynamic pages

- For route changes, keep a single instance and reuse `dispatch`/`reset`.

## License

This project is licensed under the GNU General Public License v3.0 or later (`GPL-3.0-or-later`).

Copyleft summary:
- If you distribute modified versions of this software, you must keep them under GPL-compatible terms.
- You must keep copyright and license notices.
- You must provide source code of distributed modified versions under the same license terms.

See the [LICENSE](LICENSE) file for the full legal text.
