Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/gasatrya/hoursflow/archive/refs/heads/main.zipHoursFlow — Business Hours CTA helps visitors find the right next action for a business's current weekly schedule. Configure one open CTA and one closed CTA, then present them through a shortcode or dynamic block. It is a focused, server-rendered plugin: there is no account or remote service to configure.
Who is this for?
HoursFlow CTA is for small businesses, studios, practices, shops, and service teams that want a clear schedule-aware action while keeping their labels, destinations, and status copy in WordPress.
Core features
- One shared open/closed schedule for the shortcode and dynamic block.
- Named IANA timezone support, including overnight periods.
- Per-state block overrides for label, action, status, and status visibility.
- Global colors plus WordPress's built-in block color and typography controls.
- A keyboard-operable, responsive CTA with visible focus styling.
- Server-side rendering without browser timers, polling, remote assets, or background requests.
Requirements
- WordPress 6.6 or newer.
- PHP 7.4 or newer.
- A single-site WordPress installation. Multisite and network activation are not claimed by this MVP.
Install and configure
- Download the production
hoursflow-0.1.0.zip, or copy this plugin directory towp-content/plugins/hoursflow/. - In Plugins, activate HoursFlow CTA.
- Open Settings → HoursFlow and save one complete configuration. Choose a named IANA business timezone, enter the Monday–Sunday schedule, and provide both the open and closed CTA label/action pairs. Status text is optional.
- Add the shortcode or block described below to a page.
A fresh activation is intentionally unconfigured and renders nothing until a valid configuration is saved. Settings are one atomic WordPress option and are validated before they are persisted. An invalid submission keeps the last known-good option and displays field-specific errors. The weekly-hours editor uses one keyboard-operable fieldset per weekday with visible Open/Closed state text; Closed disables its time controls, and the time rows stack on narrow screens. A live preview beside the form (stacked below it on narrow screens) lets administrators inspect either CTA state using current unsaved label, optional status, and color values without evaluating the schedule. Beneath the preview, a page-scoped developer card offers explicit links to hire Gasatrya or support HoursFlow; it does not load remote content or make a request until an administrator follows a link.
Add a CTA
Use the shortcode anywhere WordPress processes shortcodes:
[hoursflow_cta]
[hoursflow_cta hide_status="1"]
hide_status="1" is the only recognized shortcode attribute spelling and
value. It hides the status for whichever state is selected at render time; all
other attribute values, attribute containers, attributes, and enclosed content
are ignored. The default [hoursflow_cta] behavior is unchanged.
Or insert the HoursFlow CTA block (hoursflow/cta) in the block editor. The
block is dynamic and uses the same server renderer as the shortcode. Its editor
preview shows the current server-rendered output, including the current
business state.
Each block may optionally override the label, action, and/or status separately
for its open and closed state. Each state may also store the nested boolean
hideStatus flag; only strict true is valid. Overrides are sparse: every
missing or invalid content field or flag independently falls back to the
matching global state CTA. The selected state's overrides alone are applied;
the other state's values have no effect. hideStatus: true hides the selected
status even when the global or overridden status is nonblank, while a valid
blank status override continues to suppress the global status for backward
compatibility. Removing all overrides restores the legacy empty block
delimiter. Schedules remain global for every integration. Blocks additionally
expose WordPress's built-in text/background color and typography controls. A
block's colors override the global CTA link colors for that instance, while its
typography can set font size, family, weight, style, line height, letter
spacing, and text transform. These choices do not change shortcode output.
Allowed actions are root-relative URLs (for example /booking/), complete
https:// URLs, and supported tel: actions. Labels and status values are
plain text. Unsafe schemes, credentials, control characters, protocol-relative
URLs, HTML, and bare relative paths are rejected.
Schedule behavior
There is one weekly profile with one period or a closed entry per day.
- Periods are minute-precision local wall-clock intervals
[opening, closing): opening is included and the exact closing minute is closed. - A closing time earlier than the opening time is an overnight period. It belongs to the day on which it opens and carries into the following day.
- A closed following day can still be open after midnight because of the previous day's overnight period.
- Equal opening and closing times are invalid; they do not mean all-day open.
- Holidays, exceptions, split shifts, multiple periods, date ranges, and 24-hour-day settings are outside this MVP.
The saved named timezone, not the visitor, browser, server, or WordPress site
timezone, controls evaluation. UTC and named IANA regions such as
America/New_York are valid; raw UTC offsets are not. Recurring wall-clock
rules use PHP's timezone database. During a spring-forward gap, nonexistent
minutes are never evaluated and the period begins or ends at the first
existing minute at or after its configured boundary. During a fall-back
repeated hour, both occurrences of a local time receive the same schedule
result. There is no DST override or ambiguity setting.
Appearance and accessibility
The HoursFlow settings expose global CTA link background and text colors shared
by the shortcode and used as the default for every block. Individual blocks
can override both colors through WordPress's built-in color panel; content and
state overrides do not change appearance. The global color pickers display
#166534 and #FFFFFF when their corresponding saved values are blank.
Legacy blank values remain valid and use those defaults at runtime; saving the
displayed picker value stores an explicit color. Nonblank values must be
six-digit hexadecimal colors and the effective pair must meet WCAG 2.2 AA
contrast for normal text. Invalid global colors or an invalid global pair use
the complete default pair at runtime. WordPress's editor contrast checker warns
about potentially inaccessible per-block choices, but editors remain
responsible for the contrast of those overrides. There are no per-state color
overrides.
Plugin styling uses the theme's inherited typography by default. Each block can override that inheritance through WordPress's built-in typography panel; the shortcode continues to inherit theme typography. The compact, button-like baseline gives the link a minimum 44x44 CSS-pixel target, modest rounding, and responsive wrapping; status text has a small separation gap and also wraps safely. It does not set fixed dimensions or load fonts.
The output uses a native, keyboard-operable link with a visible focus style.
Open/closed state is not conveyed by color alone, and the plugin does not add a
fake button role or an aria-live announcement. Themes may style the stable
hooks .hoursflow-cta, .hoursflow-cta--open, .hoursflow-cta--closed,
.hoursflow-cta__link, and .hoursflow-cta__status.
The settings preview has keyboard-operable Open and Closed controls whose
selected state is announced. It reflects unsaved label, optional status, and
global color edits immediately, uses #166534 and #FFFFFF when color values
are blank, and is visual only: it never previews, follows, or activates the CTA
action. Preview state selection neither predicts nor changes the schedule.
Caching and limitations
State is evaluated on the server when the shortcode or dynamic block renders. HoursFlow does not use browser timers, AJAX, REST polling, cache variation, scheduled purges, or a computed-state cache. Cached page HTML can therefore remain stale for up to the site's page-cache TTL. If a site needs a maximum staleness of N minutes, configure a cache TTL of no more than N minutes or arrange boundary purges in its own caching system; HoursFlow performs neither.
The administrator is responsible for keeping status copy such as “reopen at 9 AM” accurate. HoursFlow does not calculate or translate that copy, provide holiday hours, or integrate with booking, CRM, CDNs, or page-cache systems.
Privacy and external services
HoursFlow makes no external requests and loads no remote scripts, fonts, images, stylesheets, or other assets. It collects no visitor data, sets no cookies, and performs no tracking, telemetry, scheduled network activity, or remote license checks. Ordinary user-initiated navigation is the only outbound behavior: a visitor may follow an administrator-configured HTTPS or telephone CTA, and an administrator may follow the clearly labeled Gasatrya hire or donation links on Settings → HoursFlow. The settings card loads no remote content before a link is activated.
Deactivation and uninstall
Deactivation stops HoursFlow behavior but retains the saved settings so a later reactivation restores them. Uninstall permanently deletes the HoursFlow configuration and schema marker. The plugin creates no custom tables, posts, pages, user metadata, or transient business data. Reinstalling after uninstall starts unconfigured.
Development
The repository pins its Composer and npm dependencies. Useful checks are:
From a clean checkout, install the locked dependencies and run every local gate:
composer install --no-interaction --prefer-dist
pnpm install --frozen-lockfile --ignore-scripts
composer check:php
npm run check
composer check:php validates Composer metadata, audits locked Composer
packages, checks PHP syntax and WordPress coding standards, audits static
strings, and runs PHPUnit. npm run check audits production npm packages, runs
JavaScript tests and linting, checks formatting and CSS, reproduces block/POT
assets, and verifies deterministic packaging.
A local real-WordPress run additionally needs MySQL and the exact boundary lane you want to exercise. The required integration matrix uses WordPress 6.6.7 with PHP 7.4 and WordPress 7.1.1 with PHP 8.5:
bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 6.6.7
WP_VERSION=6.6.7 composer test:integration
bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 7.1.1
WP_VERSION=7.1.1 composer test:integration
The production package is generated as dist/hoursflow-0.1.0.zip and is
intentionally ignored by Git. It contains runtime PHP, local assets, generated
block files, the translation template, and the three readable block source
files src/blocks/cta/index.js, src/blocks/cta/editor.scss, and
src/blocks/cta/block.json; other development files are excluded. The source
file src/blocks/cta/index.js is the human-readable source for the compiled
build/blocks/cta/index.js.
See the release checklist on GitHub for integration, accessibility, package, and release verification.
WordPress.org deployment
After completing the release checklist, pushing a vX.Y.Z tag starts the CI
workflow. Only after quality checks, both WordPress integration lanes, and the
production package job succeed does the deploy job publish that tested ZIP's
contents to the hoursflow WordPress.org SVN repository (trunk and tag
X.Y.Z). The 10up deploy action uses BUILD_DIR, not the raw checkout; the
local assets/ directory remains part of the plugin, not WordPress.org listing
assets. If listing assets are needed, place them in .wordpress-org/.
Set the GitHub repository Actions secrets SVN_USERNAME and SVN_PASSWORD to
WordPress.org plugin committer credentials before pushing a release tag. Do not
commit credentials. For future versions, update the plugin header/constant,
readme stable tag, package.json, changelog, POT, the versioned ZIP and artifact
references in .github/workflows/ci.yml, and the versioned docs. Review CI on
the release commit before tagging; a tag push triggers a live SVN deployment.
The tag must match vMAJOR.MINOR.PATCH and its version must match the metadata.
For readme-only listing edits after a version is published, run the manual
Update WordPress.org readme workflow on main after CI passes. It updates
SVN trunk/readme.txt and the existing stable tag's readme.txt, without
republishing code, changing the version, or touching listing assets. Editing
Git alone does not change the WordPress.org listing; re-pushing a published tag
will not update it.