Widgets
Twenty-two web components that put the API in a page with one script tag: a day's panchang and muhurta, a month as a calendar, the planets now, drawn charts, a birth form with a tabbed kundli report, Ashtakoot matching, a horoscope by sign, written readings and eleven birth calculators. No framework and no backend. The calculation stays in the API; the widgets add the requests, a page cache, error states, English and Hindi, and the markup.
The widget catalogue lists all 22 in their four groups with the routes and the credits each one costs. This page is the reference: every attribute, the design system, and the parts that need a server.
The script tag#
<script src="https://cdn.kaaljyoti.com/widgets/v1.js" data-key="kj_pub_…" defer></script>
<kj-panchang city="delhi"></kj-panchang>
<kj-horoscope sign="aries"></kj-horoscope>
<kj-kundli-form></kj-kundli-form>
That is the whole install. v1.js is a loader of about 1 KB: it looks for
kj- tags on the page and fetches each widget's code the first time the page
has one, so a page with a panchang never downloads the kundli report. With
defer, elements already in the markup load as soon as the script runs, and
elements added later load when they appear.
The script reads these attributes. Each sets a default for every widget on the page; an attribute on an element wins.
| Attribute | Value | Default |
|---|---|---|
data-key | A publishable key, kj_pub_… | — (required, unless every request is proxied) |
data-lang | en or hi | en |
data-theme | auto, light or dark — see modes | auto |
data-preset | classic, modern, minimal or traditional — see presets | classic |
data-font | inherit for your page's own font — see fonts | the widget's own |
data-sign-icons | A sign-icon theme, or your own images — see zodiac sign icons | element |
data-powered-by | shown or hidden — see powered by | hidden |
data-time-format | 12 or 24, for the birth forms' time | 12 |
data-remember | off to stop the forms remembering the last entry on the device | on |
data-place-provider | auto, google, photon or kaaljyoti — see place search | auto |
data-google-maps-key | Your Google Maps key, for the place search | — |
data-photon-url | Your own Photon server, for the place search | https://photon.komoot.io |
data-pricing-url | Where the monthly-limit and plan cards send a site owner; off for no link | https://kaaljyoti.com/api/pricing |
data-proxy | Your site's proxy endpoint — see the server proxy | — |
data-proxy-all | Present: send every request through the proxy, for a page that carries no key | absent |
data-proxy-docs | Where the "needs a server connection" note sends a site owner; off for no link | this page's #proxy |
data-pdf | basic, professional, both, or on — the PDF editions your proxy relays | — (no PDF button) |
data-chunks | The directory the widgets' code is loaded from — see hosting the files | beside v1.js |
data-base | The API origin, for staging — see staging | https://api.kaaljyoti.com |
The same code is published to npm as @kaaljyoti/widgets, for a page with a
bundler — see from npm.
The key and the origin rule#
Create a publishable key in the dashboard
and list the origins it will be used from — exactly, including scheme and
port: https://example.com, https://www.example.com,
http://localhost:3000. A publishable key used from an origin it does not
list gets 403 forbidden_origin, and the widget says so in the page, with the
origin to add.
The key is meant to be readable; the origin list is what protects it. See authentication.
Attributes every widget takes#
| Attribute | Value | Default |
|---|---|---|
lang | en or hi | the script's data-lang |
theme | auto, light or dark | the script's data-theme |
preset | classic, modern, minimal or traditional | the script's data-preset |
font | inherit, or system for the widget's own | the script's data-font |
sign-icons | element, glyph, devanagari or custom | the script's, or element |
heading | A title for the card, or off to drop the header | the widget's own title |
frame | none to draw the widget without its card | the card |
pricing-url | As data-pricing-url, for this widget | the script's |
powered-by | shown or hidden | the script's |
A place is given one of two ways: city, one of eight bundled ids —
delhi, mumbai, kolkata, chennai, bengaluru, hyderabad, jaipur,
varanasi — or a lat and lon pair, which wins over city, with an
optional IANA timezone (leave it out and the API derives it) and a place
label.
A birth is a datetime — the clock on the wall where the birth happened,
YYYY-MM-DDTHH:MM:SS, never UTC; see time zones — and a place given the same way. From script,
element.birth = { datetime, timezone, latitude, longitude, place } sets them
in one go on any widget that takes a birth.
Daily widgets#
A place and a day; no birth. Without a place, the panchang says so and asks for nothing; the other five default to New Delhi.
<kj-panchang> — 1 call#
The day's five limbs as tiles — tithi (with every tithi that touches the day: "until 14:56, then Panchami"), nakshatra and pada, yoga, karana and vara — the masa with the Vikram Samvat year, sunrise and sunset, and the day's windows on a timeline from sunrise to the next sunrise, with the same windows and disha shool as a list under it.
<kj-panchang city="varanasi" lang="hi"></kj-panchang>
| Attribute | Value | Default |
|---|---|---|
city, lat, lon, timezone | The place, as above | — |
place | A label for the heading | the city's name |
date | YYYY-MM-DD, or today | today |
show | Space-separated subset of header tithi nakshatra yoga karana sun windows masa | all |
today sends no date, which the API reads as today at the place — the same
civil day wherever the reader is.
<kj-muhurta> — 1 call#
The day's choghadiya and windows (POST /v1/panchang/muhurta): a timeline
from sunrise through sunset to the next sunrise with the sixteen choghadiyas
on one lane and Rahu kaal, Yamaganda, Gulika and Abhijit on the other; the
windows as a list; and the choghadiyas as a table with a Day / Night switch
that opens on the half the place is in now, the running one marked. The same
place and date attributes as <kj-panchang>, and no show.
It is its own request, so beside a <kj-panchang> for the same place and day
it is a second call. Two muhurtas for one place and day are one.
<kj-panchang-month> — 1 call per month shown#
A month as a calendar: one cell per day with the tithi at sunrise and the nakshatra; Purnima, Amavasya and the Ekadashis marked, with a key. A click opens the day's full panchang under the grid — every tithi, nakshatra, yoga and karana that touches the day with its end time, sunrise and sunset, the masa and the Vikram Samvat year. The API gives no festival list, so none is shown.
| Attribute | Value | Default |
|---|---|---|
month | YYYY-MM; the ‹ › buttons step it, a call per month | this month at the place |
masa | purnimanta or amanta; the switch flips it, no call | purnimanta |
proxy | Your site's proxy endpoint | the script's |
/v1/panchang/month is a heavy route: 20 credits a month shown, and ten
requests a minute per key. The widget calls it with the publishable key, or
through your proxy when the page has one, which can cache the
month.
<kj-calendar> — 2 calls per date#
The Hindu calendar converter. A date in (day, month and year selects; date,
default today), and out: its Vikram Samvat year, the masa in the purnimanta
and the amanta reckonings, the paksha, the tithi at sunrise and the one after
it, the vara and the nakshatra. POST /v1/panchang for the day — shared with
a <kj-panchang> for the same place and day — then
POST /v1/calendar/vikram-samvat at its sunrise.
<kj-transits> — 2 calls#
"Planets now": the chart of this minute at the place, with its North / South
/ Circular switch, and a table of each graha's sign, degree, nakshatra and
pada and retrograde motion, with the lagna rising there. POST /v1/transit/now
and POST /v1/kundli/chart for the same instant; "Refresh" asks again.
chart="off" drops the chart and its call; chart-style and size pass to
it.
<kj-ephemeris> — 1 call per month and zodiac#
A month of daily longitudes at 00:00 UT: a row a day, a column a graha (and
the ascendant at the place), each cell the degree in the sign, the sign's
icon where it changes, ℞ while a graha is retrograde; the month's ingresses
and stations as a list under it. month (default this month) and
system="tropical" (default sidereal; the switch flips it, one call). Heavy,
like the month of panchang: 20 credits, ten a minute, and through your proxy
when the page has one.
Calculators#
<kj-chart> — 1 call#
A drawn kundli, inlined as SVG so it takes the page's colours.
<kj-chart
datetime="1990-05-14T10:30:00"
timezone="Asia/Kolkata"
lat="28.6139"
lon="77.209"
place="New Delhi"
style="north"
size="360"
></kj-chart>
| Attribute | Value | Default |
|---|---|---|
datetime | The birth's wall clock, YYYY-MM-DDTHH:MM:SS | — (required) |
lat, lon, timezone, city, place | The birth place, as above | — |
style | north, south or circular | north |
chart-style | The same as style, for a page that needs style for CSS | — |
size | 200 to 2000 pixels; a value outside is clamped | 360 |
varga | d1, d9, d10, … d60 | d1 |
show-degrees | false to drop the degree labels | true |
Without a datetime and a place it says there is no birth and asks for
nothing.
A language or a style change on a chart is another call. The other
widgets get both languages in one answer and repaint when lang changes; a
chart's labels are drawn into the SVG by the API, so another language — or
another style — is another drawing.
The birth calculators#
Eight calculators here, and three of the reports below (life areas, varshphal and the dasha reading), take one person's birth, two ways:
- A form (the default): the kundli form's fields without gender — name (optional), date, time and a place search. Nothing is sent until a visitor submits; then the form folds into one line ("Asha · 14 May 1990, 10:30 · Varanasi — Edit details") and the result opens under it. The entry is remembered on the device, so a visitor finds it filled in on the next calculator.
- Attributes:
datetimeandlat/lon(orcity),timezone,place, andnamefor the header — or.birthfrom script. No form; the result at once.
All of them take time-format, remember, place-provider, photon-url,
google-maps-key and city (which pre-fills the place) as the kundli form
does, and disclaimer, disclaimer-name and disclaimer-url for the
readings they draw. Each part of a result has its own state, so a refused
request shows its card in its place and the rest stands.
| Element | Shows | Calls per birth |
|---|---|---|
<kj-moon-sign> | The Moon sign and its degree, the janma nakshatra and pada, the nakshatra reading | 2 — /v1/kundli, /v1/reports/nakshatra; with reading="off", 1 |
<kj-lagna> | The lagna and the ascendant's degree in it, the Moon sign, the lagna reading | 2 — /v1/kundli, /v1/reports/lagna; with reading="off", 1 |
<kj-manglik> | Manglik or not, the house Mars is in from the lagna, the classical mitigation; the other doshas the chart forms, with their grahas | 1 — /v1/kundli/yogas |
<kj-sade-sati> | Whether sade sati or a dhaiya is running now; a calendar year's phases — rising, peak, setting, dhaiya — with Saturn's sign and dates; a stepper | 1 per year shown — /v1/kundli/sade-sati |
<kj-dasha> | The running periods, the mahadashas as a band and a table, drill-down to pratyantardasha; a Yogini switch (yogini="off" hides it) | 1 — /v1/kundli/dasha; Yogini 1 more |
<kj-vargas> | A select of D1 to D60; the varga's chart and each graha's sign in it beside its D1 sign, vargottama marked. varga (default d9) | 1 — /v1/kundli/vargas, plus 1 /v1/kundli/chart per varga or style |
<kj-kp> | Tabs: cusps and planets with sign, star, sub and sub-sub lords; significators by house and by graha; ruling planets. tab opens one first | 1 — /v1/kp/chart |
<kj-strength> | Tabs: Shadbala against the required minimum and the six strengths; Ashtakavarga, the SAV square and each graha's BAV | 1 per tab opened — /v1/kundli/shadbala, /v1/kundli/ashtakavarga |
Sade sati is priced as a scan across time — 10 credits a year shown — and the rest at 1 credit a calculation and 5 a reading.
Reports#
Written text an astrologer wrote, in English or Hindi, ending with the disclaimer. Every report is on every plan, at 5 credits. Nothing is scored: there are no percentages, star ratings or lucky numbers, because the API has none and the widgets invent none.
<kj-horoscope> — 1 call per sign, period and day#
For a Moon sign, the horoscope over a period: one summary, then a card for each of five areas — work, money, relationships, health, education — each with a level, Favourable, Mixed or Needs care, and a short text that dates any change inside the period. A reader picks the sign from the twelve, drawn as sign icons, and the period from four tabs (and, for a day, yesterday, today or tomorrow). Each pick writes the element's own attribute, so a page can read back what was chosen.
| Attribute | Value | Default |
|---|---|---|
sign | aries … pisces — preselects the picker | — (no call) |
period | daily, weekly, monthly or yearly | daily |
date | YYYY-MM-DD, or today; a week runs seven days from it | today |
timezone | The IANA zone the days are counted in | Asia/Kolkata |
show-basis | Present: also list the transits behind it, folded away | absent |
disclaimer | off to drop the closing line | the API's line |
disclaimer-name | Your astrologer's name, for the closing line | — |
disclaimer-url | A link shown with the name, http or https | — |
Without a sign it shows the picker and asks for nothing until one is
picked.
<kj-reading> — 1 call#
What a lagna (type="lagna", the default) or a janma nakshatra
(type="nakshatra") says about a person — for one the reader picks, one you
preset, or a birth's — or one of the personal readings of a birth.
| Attribute | Value | Default |
|---|---|---|
type | lagna, nakshatra, house_lords, grahas, yogas, vimshottari, varshphal, life_areas or kundli | lagna |
sign | aries … pisces, for a lagna | — |
nakshatra | ashwini … revati (purva_phalguni, …) | — |
datetime, lat, lon, city, timezone, place | A birth, as on <kj-chart> | — |
year | The varshphal's year, from the birthday in it (1800–2400) | running |
parts | For type="kundli": the readings to include, spaces or commas | all eight |
disclaimer, disclaimer-name, disclaimer-url | As on <kj-horoscope> | the API's |
A preset sign or nakshatra needs no birth, for a "know your lagna" page.
With neither a preset nor a birth it shows a picker. The personal readings
need a birth — without one they say so and ask for nothing:
type | Route | What it draws |
|---|---|---|
house_lords | POST /v1/reports/house-lords | Twelve houses in order: "1st house · Gemini · lord Mercury in the 9th", then the reading |
grahas | POST /v1/reports/grahas | Nine grahas: "Sun · Aries · 10th house", then what it says in that sign and in that house |
yogas | POST /v1/reports/yogas | Each yoga that forms: its name, the grahas in it, the text |
vimshottari | POST /v1/reports/vimshottari | Each mahadasha with its dates, a level and the text; the running one marked |
varshphal | POST /v1/reports/varshphal | The year from the birthday in year: a summary, seven areas, and the year's periods as dated chips |
life_areas | POST /v1/reports/life-areas | The summary, then a card for each of eleven areas of life, each with a level |
kundli | POST /v1/reports/kundli | Several of the above in one request — parts names them — each drawn as above, with one disclaimer |
Each is 5 credits; type="kundli" is 5 per part, so parts="lagna, nakshatra, yogas" is 15 and all eight are 40. Without year the varshphal is the one
running now: this year's from the birthday on, last year's before it.
<kj-reading
type="vimshottari"
datetime="1990-05-14T10:30:00"
timezone="Asia/Kolkata"
lat="28.6139"
lon="77.209"
></kj-reading>
<kj-life-areas>, <kj-varshphal>, <kj-vimshottari-reading>#
Three readings as birth calculators, with the form in front of them:
| Element | Shows | Calls per birth |
|---|---|---|
<kj-life-areas> | The life-areas reading: one summary, eleven areas with a level each | 1 — /v1/reports/life-areas |
<kj-varshphal> | A year selector (year, default the one running now); when the year begins, varsha lagna, muntha and its house, year lord; the varsha kundli; the year's reading | 3 per year — /v1/varshphal, /v1/kundli/chart, /v1/reports/varshphal |
<kj-vimshottari-reading> | Each mahadasha from birth to eighty with its dates, level and reading, the running one marked | 1 — /v1/reports/vimshottari |
A varshphal year is 7 credits: two calculations and a reading.
The disclaimer#
Every reading and horoscope ends with "These predictions are indicative. For
a reading of your own chart, consult an astrologer." (and its Hindi), small
under the text. disclaimer-name="Acharya …", with an optional
disclaimer-url, names your own astrologer instead; disclaimer="off" drops
it — for an astrologer handing a reading to their own client.
Kundli and match#
<kj-kundli-form> — 3 calls per submit, then per tab#
A birth form and, on submit, a report in tabs.
<kj-kundli-form tabs="overview charts planets dasha" chart-style="south"></kj-kundli-form>
On submit the form folds into one line — "Asha · 14 May 1990, 10:30 · Varanasi" with an "Edit details" button — and the report opens under it, scrolled into view:
| Tab | Shows | Calls, on first open |
|---|---|---|
| Overview | Lagna, Moon sign, nakshatra and pada, the running mahadasha and antardasha, the tithi, the yogas, the lagna reading | 3 — /v1/kundli, /v1/kundli/dasha, /v1/reports/lagna |
| Charts | D1, D9 and the Moon chart, North, South or Circular, and the bhava chalit table | 1 per chart and style shown; 1 for chalit |
| Planets | Sign, degree, nakshatra and pada, house; ℞, dignity and vargottama badges | 2 — /v1/kundli/pace, /v1/kundli/vargas |
| Dasha | The Vimshottari mahadashas and the running one's antardashas | none (the overview's) |
| Life areas | The life-areas reading | 1 — /v1/reports/life-areas |
| Readings | Only with readings: the readings it names | 1 per reading |
Only the overview loads on submit; each other tab asks the first time it is opened. A visitor who reads only the overview costs 7 credits — two calculations and a reading. A tab whose request is refused shows its card in its place; the rest of the report is unaffected.
| Attribute | Value | Default |
|---|---|---|
tabs | Space-separated: overview charts planets dasha life-areas | all |
show | The first release's spelling: summary is Overview and Planets, chart Charts | — |
city | A bundled city to pre-fill the place search with | none |
time-format | 12 or 24 | 12 |
remember | off to not remember the last entry on the device | on |
chart-style, size | Passed to the charts | as <kj-chart> |
readings | Adds a Readings tab: alone, the lagna and nakshatra; or a list of reading types | off |
disclaimer, disclaimer-name, disclaimer-url | Passed to the readings | the API's line |
pdf | off to hide the PDF button here, or a narrower list of editions | the script's |
place-provider, photon-url, google-maps-key | The place search | auto |
readings takes any of lagna, nakshatra, house_lords, grahas,
yogas, vimshottari, varshphal and life_areas, spaces or commas, drawn
in that order, each 5 credits. With PDFs on, the report has
a "Download PDF" bar under the name.
<kj-match-form> — 3 calls per submit#
Ashtakoot guna milan. Two birth fieldsets, bride and groom, each with the kundli form's fields and each remembered separately, side by side on a wide card and stacked on a phone. The result: the total out of 36 as a ring with the verdict (green from 25, gold from 18 to 24, red below 18), each person's Moon sign and nakshatra, the eight kootas with their points, a bar and what each looks at, and each side's Mangal dosha, with a warning when only one has it.
| Attribute | Value | Default |
|---|---|---|
city | A bundled city to pre-fill both places with | none |
time-format, remember, pdf | As on <kj-kundli-form> | |
place-provider, photon-url, google-maps-key | The place search | auto |
Each submit is one POST /v1/match/ashtakoot with both births and one
POST /v1/kundli per person for the Moon signs: 3 credits. Resubmitting the
same pair costs nothing, and a lang change repaints from the answers held.
The birth form#
Both forms, and every calculator, ask for a birth the same way:
- Name and, on the kundli form, gender — both optional; gender is not sent, because the API takes none.
- Date as day, month and year selects. A real calendar check: 31 February is refused.
- Time as hour, minute and AM / PM, or 0–23 with
time-format="24". AM / PM has no default, because a silent AM is a wrong chart. - Place as one search box. After a pick, the place, its coordinates and its time zone (or "worked out from the coordinates") show under it, and "Edit coordinates" opens latitude, longitude and an optional zone.
Each missing field says so under its own group, in English or Hindi, and
nothing is sent until the date, the time and the place are all there. A valid
submit stores the entry on the device, and the next visit fills it in with a
"Clear" link; remember="off" (or data-remember="off") turns that off and
forgets what was stored.
Place search#
The place field searches from the third letter, after a pause in typing. A
pick fills the coordinates; "Edit coordinates" lets a visitor type them
instead. Where it searches is place-provider on the form or
data-place-provider on the script:
| Value | Searches |
|---|---|
auto (default) | Google Places with a Google Maps key, else Photon |
google | Google Places only; needs a key |
photon | Photon only, even with a Google Maps key |
kaaljyoti | Kaal Jyoti's own GET /v1/places only: no third party is asked anything |
- Photon is komoot's free, keyless place search over OpenStreetMap, and it
finds villages. Its searches cost no Kaal Jyoti credits, and the list
credits "© OpenStreetMap contributors", as OpenStreetMap's licence asks. A
busy site can run its own Photon and point
photon-urlat it. - Google Places needs your own key,
google-maps-key="AIza…"ordata-google-maps-key, with Places API (New) and Maps JavaScript API enabled and restricted to your site. Google bills its searches to your Google account; they cost no Kaal Jyoti credits. The list shows the attribution Google requires. - Kaal Jyoti is every town and city of 1,000 people or more, in English or Hindi, with its time zone. Each search is 1 credit.
If Photon or Google fails, is slow or refuses the key, the form goes on with
the next one — Google to Photon under auto, and anything to /v1/places —
for the rest of the page. A Photon or Google pick sends only coordinates,
and the API works out the historical time zone there.
What a visitor types in the place box goes to Photon or Google, whichever
searches. Say so in your privacy policy, or set place-provider="kaaljyoti".
What a page costs#
Every widget runs in each visitor's browser, so every call is per page view. The catalogue lists each widget's routes and credits; in short:
- A calculation is 1 credit — a panchang, a muhurta, a chart, a kundli, a
dasha, a match, a place search on
/v1/places. - A reading or a horoscope is 5. A sade sati year is 10. A month of panchang or ephemeris is 20. A kundli PDF is 1,000 and a match PDF 500.
- Nothing is called until there is something to show: the forms and calculators until a submit, the horoscope until a sign, a reading until a sign, a nakshatra or a birth.
- Identical requests on a page are made once, whether the first is still in flight or already answered. Two identical charts are one call, and submitting the same birth again costs nothing.
A refused request costs nothing. An answer from the API's cache costs the same as a fresh one, which is why the page-level memo matters. A page seen ten thousand times a day with one panchang on it is ten thousand credits; if that is your page, render it on your server and cache it — the WordPress plugin does that in its server mode. See Credits per API.
Theming#
Every widget renders into an open shadow root, so your page's dl and
table rules cannot take a widget apart — and a widget's rules cannot reach
your page. Two ways in are supported: the --kj-* custom properties and
::part().
The card#
Every widget is a card: a header band with a title and a line for the place
and date, the body, and a footer with the reading's disclaimer and the
powered-by link. heading="Aaj ka panchang" replaces the title and
heading="off" drops the header; frame="none" draws the widget without its
card — no border, background or footer — for a page that frames it itself.
Presets#
preset on an element, data-preset on the script, picks one of four looks —
the same four as the PDF templates, so a
site's widgets and reports match:
| Preset | Look |
|---|---|
classic (default) | Cream and maroon, serif headings, a thin gold rule |
modern | Teal and amber, sans headings on a solid accent band |
minimal | Ink on white, hairlines only |
traditional | Maroon, saffron and gold; a tinted header with a gold rule and a saffron diamond |
A preset sets the accent family, the heading face and the header; the mode below still sets light, dark or auto, and every custom property still wins.
Modes#
theme | What it looks like |
|---|---|
auto (default) | Blends in. No card background; text, labels and chart lines take the page's text colour, so it reads on a light page and a dark one. Accent and graha colours lighten on a dark page. |
light | The preset's light card with dark text. |
dark | A dark card with light text and lightened graha colours. |
auto reads the page itself: the widget looks at the text colour it
inherits, and light text means a dark page (it sets data-scheme="dark" on
itself). Before that is known, CSS light-dark() follows the page's
color-scheme, and a browser without it follows the visitor's
prefers-color-scheme. A dark site no longer has to declare
color-scheme: dark for the widgets to read on it.
data-theme on the script sets a default for every widget that has no
theme of its own. A theme written on an element is never replaced, and
<kj-kundli-form> passes its theme to the charts it draws.
Fonts#
By default a widget sets its own type: the system UI face for body text, Noto
Serif for the classic and traditional headings, and a Devanagari stack (Noto
Sans Devanagari, Mukta, …) for Hindi. No web font is loaded. font="inherit"
(or data-font="inherit") uses your page's own font for everything, headings
included; a Hindi card puts the Devanagari faces behind it, since few Latin
faces carry Devanagari. --kj-font and --kj-font-heading win over both.
Size and width#
A widget lays itself out by its own width, with container queries, not by
the screen's: in a 300px sidebar it takes the phone layout on a desktop page,
and tables too wide for the card turn into one small card per row. The base
size is the page's, between 15 and 18px (--kj-size fixes it). On a wide
block the card stops at --kj-max-width (72em) and centres itself; readings
stop at a reading width inside it.
Custom properties#
Colour and type cross the shadow boundary as custom properties. Set them on
the element, on a wrapper or on :root. They win over every mode and preset,
so a mode is a starting point, not a lock:
kj-panchang,
kj-chart {
--kj-bg: #ffffff;
--kj-text: #1a1a1a;
--kj-accent: #7a1f2b;
--kj-font: 'Inter', system-ui, sans-serif;
}
| Property | What it colours |
|---|---|
--kj-bg | The chart's ground |
--kj-surface | The widget's own background |
--kj-text | Body text |
--kj-muted | Labels, captions, secondary text |
--kj-line | Chart rules and table borders |
--kj-lagna | The lagna, and the accent's default |
--kj-accent | Headings, tabs and the submit button |
--kj-accent-2, --kj-on-accent | Ornaments (gold, saffron), and text on the accent |
--kj-tint, --kj-border, --kj-shadow | Tiles and the header band, hairlines, the card's shadow |
--kj-planet | A graha with no colour of its own |
--kj-retro | The ℞ marker |
--kj-planet-sun, -moon, -mars, -mercury, -jupiter, -venus, -saturn, -rahu, -ketu | One per graha, in the chart and the planet tables |
--kj-good, --kj-bad | Auspicious and inauspicious windows; --kj-good is also a Favourable level |
--kj-care | The Needs care level |
--kj-font, --kj-font-heading | Body type and heading type |
--kj-size, --kj-radius, --kj-max-width | The base size, the corner radius, and where a card stops growing |
--kj-sign-* | The sign icons — see zodiac sign icons |
The chart's names are the ones the API's own chart SVG declares (see embed recipes), so a chart and the widgets around it take one theme.
Parts#
For structure rather than colour, style the named parts with ::part():
card, header, title, subtitle, body, footer, tiles, tile
(each also tile-<name>), label, value (each also <name>-label and
<name>-value, e.g. tithi-value), timeline, legend, windows,
window, chart, form, submit, result, tabs, tab, panel,
segmented, segment, badge, summary, areas, area, planets,
yogas, gauge, kootas, loading, error, plan-required, plan-owner,
disclaimer, powered-by-row, powered-by.
The newer widgets add collapsed, birth-summary, edit, calendar,
day, day-detail, month-masa, marks, stepper, verdict, phases,
phase, crumbs, periods, period, band, running, varga-table,
cusps, significators, ruling, shadbala, sav, sign-cell, bav,
ephemeris, events, transits, proxy-required and proxy-owner. Every
sign's icon is sign-icon, with sign-icon-l in the horoscope's picker and
sign-icon-m beside a value or a heading.
kj-panchang::part(tithi-value) {
font-weight: 700;
}
kj-panchang-month::part(day) {
min-height: 6em;
}
Zodiac sign icons#
Wherever a sign is shown — the horoscope's picker, the kundli overview, the lagna and Moon-sign calculators, the match result, and the planet, transit and varga tables — it carries an icon the widget draws, not a font character, so it looks the same whatever fonts the visitor has. Four themes:
| Theme | What it draws |
|---|---|
element (default) | The sign's symbol on a rounded tile, coloured by its element: fire, earth, air or water |
glyph | The same symbol in a thin ring, in the accent colour |
devanagari | The Hindi name (मेष … मीन) in a double-ring seal |
custom | Your own images |
In a table the icon is the compact form: the symbol alone, in its element's
colour. sign-icons on an element (or any element around it) chooses the
theme there; data-sign-icons on the script sets it for the page.
Your own images are a URL template with {sign}, which becomes aries …
pisces, or a map:
<script
src="https://cdn.kaaljyoti.com/widgets/v1.js"
data-key="kj_pub_…"
data-sign-icons="https://example.com/signs/{sign}.png"
defer
></script>
KJWidgets.configure({
signIcons: { aries: 'https://example.com/ram.webp', taurus: 'https://example.com/bull.webp' },
});
A map may also name the page's default theme as theme — { theme: 'glyph', aries: … } — and the images are then used only where an element asks for
sign-icons="custom". Each image is drawn as an <img> with the sign's name
as its alt text, never inlined, so an SVG of yours cannot put markup or script
into the widget. Only https:// URLs and paths on your own site are used;
anything else is ignored. A sign the template or map does not give, or an
image that fails to load, falls back to the element icon. Square images look
best: about 48px in the picker and 20–32px elsewhere.
Colours are custom properties like any other:
| Property | What it colours |
|---|---|
--kj-sign-fire-bg, --kj-sign-earth-bg, --kj-sign-air-bg, --kj-sign-water-bg | The tile behind an element icon |
--kj-sign-fire-circle, -earth-circle, -air-circle, -water-circle | Its soft inner circle |
--kj-sign-fire-ink, -earth-ink, -air-ink, -water-ink | The symbol's stroke, the seal's rings and name |
--kj-sign-glyph, --kj-sign-ring | The glyph theme's symbol and its ring |
The defaults follow the preset and the mode: traditional leans to saffron
and gold, minimal is one ink with no tint, and a dark card gets deep tiles
with light strokes.
A styling guide#
- One brand colour: set
--kj-accentalone. Tints, rules, the selected tab and the text on buttons are worked out from it. - Your font:
font="inherit", or--kj-fontfor a specific stack. - A dark site: leave
themeunset;autoswitches to its dark hues on a dark page. Or settheme="dark"for an opaque dark card. - Frame it yourself:
frame="none", andheading="off"for the header.
kj-panchang-month,
kj-dasha {
--kj-accent: #0f766e;
--kj-radius: 6px;
}
kj-kundli-form::part(card) {
box-shadow: none;
border-width: 2px;
}
Powered by#
A "Powered by Kaal Jyoti" link under a widget is opt-in, on every plan. Add
data-powered-by="shown" to the script tag to show it under every widget, or
powered-by="shown" to one widget; powered-by="hidden" leaves it out of one
widget when the script shows it. Without either, there is no link.
States#
A widget never shows a raw error. Each state is a small card inside the widget:
| State | When | What it says |
|---|---|---|
| Loading | A request is in flight | A shimmer shaped like the widget |
| No place, no birth | Nothing to ask for yet | A line saying so; nothing is sent |
| Monthly limit reached | 402 quota_exceeded: the account's credits for the month are used up | "Monthly limit reached", and a small line for the site owner linking pricing-url to add credits or upgrade |
| Available on a paid plan | 403 plan_required: a PDF on the Free plan | That PDFs need a paid plan, with the same owner line |
| Needs a server connection | A route closed to publishable keys, and no proxy on the page | That the widget needs the WordPress plugin or a server proxy, with a link for the owner (proxy-docs) |
| Error | Anything else | One translated line. forbidden_origin names the origin to add; a 429 is retried once after Retry-After |
Inside the kundli report a state fills only the tab whose request was refused.
Events#
All three bubble and cross the shadow boundary, so one listener on document
hears every widget.
| Event | Fired by | detail |
|---|---|---|
kj-ready | Every widget, after its main answer | The response data |
kj-error | Every widget, after a failure | code, status, message, requestId |
kj-submit | <kj-kundli-form> and the birth calculators | The birth: datetime, timezone, latitude, longitude, place |
kj-submit | <kj-match-form> | bride and groom, each a birth as above |
document.addEventListener('kj-error', (event) => {
console.warn(event.detail.code, event.detail.requestId);
});
The code is one of the API's — see errors.
Languages#
English (en) and Hindi (hi). The labels, the error lines and every name
the answer carries — tithi, nakshatra, yoga, karana, paksha, weekday, the
lunar month, the signs, and the readings' text — are shown in the language
asked for. Set it for the page with data-lang or per widget with lang. A
widget can be switched after it has drawn: requests ask for both languages,
so only a chart, whose labels are drawn into the SVG, spends a call to do it.
The server proxy
Some routes are closed to publishable keys whatever the plan — the PDFs,
/v1/transit/scan and /v1/match/batch. A widget that needs one sends its
request to your site's own endpoint, which calls the API with your secret
key. The month routes are open to publishable keys, but the two month widgets
use the proxy when the page has one, so your site can cache each month.
<script
src="https://cdn.kaaljyoti.com/widgets/v1.js"
data-key="kj_pub_…"
data-proxy="/wp-json/kaaljyoti/v1/proxy"
defer
></script>
data-proxy on the script, or proxy on an element, is an https:// URL or
a path on your site. The WordPress plugin provides one. To write your own:
- The request.
POST {proxy}withContent-Type: application/jsonand no key. The body is{"path": "/panchang/month", "body": {…}}:pathis the API route after/v1, andbodythe JSON the widget would have sent. The page's cookies go with it (credentials: same-origin). - What your proxy must do. Allow only the paths it means to serve — the
PDFs, the month routes it caches — and refuse anything else. Refuse a body
over 16 KB. Call
POST https://api.kaaljyoti.com/v1{path}withAuthorization: Bearer kj_live_…(never the key in the query), and pass the widget'sX-KJ-Clientheader through. Rate-limit each visitor: the heavy routes cost your credits, and the API's ten-a-minute limit is per key, shared by every visitor. It may cache a month's answer bypathand body; twelve hours is safe. - The response. Relay the API's status code and JSON body as they
are, with
X-KJ-Plan,X-KJ-Request-IdandRetry-Afterwhen present. The widget reads it exactly as a direct answer, so a402 quota_exceededbecomes the monthly-limit card. Never putX-KJ-Credits-Remainingin a page: it is the account's balance. - Your own refusals use the same envelope:
{"status": "error", "error": {"code": "proxy_path_not_allowed", "message": "…"}}with403,proxy_rate_limitedwith429andRetry-After, orproxy_errorwith502. - A nonce is optional. The endpoint is read-only and allowlisted, and a
nonce would break page caching; if you want one, put it in the URL you write
into
data-proxy, and the widget sends that URL as given.
data-proxy-all sends every request through the proxy, for a page that
carries no key at all. A widget on a closed route with no proxy shows "Needs
a server connection" and sends nothing.
PDF downloads#
The kundli report and the match result can offer a printable PDF
(/v1/pdf/kundli, /v1/pdf/match). A PDF is never made for a publishable
key, so it always goes through your proxy, and the proxy has to say
it relays them: data-pdf="basic professional" (or basic, professional,
or on for basic) names the kundli editions your site offers.
With that, the kundli report (under its name) and the match result (under
its score) show a "Download PDF" bar: the edition when there is a choice, the
language, and the button. The widget sends
{"path": "/pdf/kundli", "body": {birth, options: {language}, edition, name, template, chart_style}}
— template is the widget's preset, since the four presets are the four PDF
templates — and saves the file under the name the API gave it. The PDF
carries your account's branding from the dashboard; the page cannot change
it. pdf="off" on an element hides the bar there, and a list narrows the
editions.
A kundli PDF costs 1,000 credits and a match PDF 500, and each uses one PDF of the month's allowance: Starter 50, Growth 200, Scale 500, Enterprise 2,500. PDFs are on every paid plan and not on Free; a site on Free gets the "Available on a paid plan" card, and one past its PDFs for the month a line saying so. See PDFs.
From npm#
Not on npm yet.
@kaaljyoti/widgetsis published to npm with the next widgets release; until then, use the script tag. The code below is how it will be used.
npm install @kaaljyoti/widgets
import { configure, define } from '@kaaljyoti/widgets';
configure({ key: 'kj_pub_…', lang: 'en', theme: 'auto', preset: 'classic' });
define(); // registers all 22; safe to call twice
Importing the module registers nothing until define() is called, so a
bundler or a framework stays in control, and on a server render it evaluates
without touching customElements. Every script attribute has a configure()
option of the same name in camelCase: signIcons, placeProvider,
googleMapsKey, proxyUrl, pricingUrl, timeFormat, remember, pdf,
baseUrl. The 22 element classes (KjPanchang, KjKundliForm,
KjHoroscope, …) are exported as well.
Versions and hosting#
Which file to load#
https://cdn.kaaljyoti.com/widgets/v1.js is the rolling loader: each new
release replaces it, so a site on v1.js gets fixes and new widgets without
changing its tag. A breaking change would get a new name, not a new v1.js.
Browsers cache it for five minutes.
A site that wants to choose when it upgrades loads a pinned copy instead:
each release also publishes https://cdn.kaaljyoti.com/widgets/v<version>.js,
named with the widgets' full version number. A pinned file never changes once
published, and the widget code it loads is never deleted, so a pinned page
keeps working exactly as it is until you change the tag.
Hosting the files#
v1.js loads each widget's code from the directory it was loaded from, the
first time the page has one of its tags. To serve the widgets from your own
site, copy the loader and every chunk the build lists in cdn-files.json
into one directory, flat. The chunks are ES modules, fetched with CORS: serve
them from the page's own origin, or send Access-Control-Allow-Origin with
them. If a script optimiser moves or combines v1.js, put the directory on
the tag as data-chunks="https://example.com/path/to/widgets/". The
WordPress plugin ships the files this way.
From script, KJWidgets.define() loads every widget at once, and
KJWidgets.load('kj-chart') loads one — for a tag inside another shadow
root, which the loader cannot see.
Staging#
One bundle, one attribute:
<script
src="https://cdn.kaaljyoti.com/widgets/v1.js"
data-key="kj_pub_…"
data-base="https://api-staging.kaaljyoti.com"
defer
></script>
Give an origin only. /v1 is added for you, and a trailing /v1 you paste in
is removed. From npm it is configure({ baseUrl }).
What it never does#
- It never uses a secret key. Only
kj_pub_…works from a page. The API refuseskj_live_…andkj_test_…in a query string, and the widgets never send anAuthorizationheader. - It never calls a closed route with your publishable key. The PDFs, the transit scan and the batch match go through your proxy or not at all. The month routes are open to publishable keys and are called directly when there is no proxy.
- It never asks for
embed_font. The chart is drawn with the web font, which is what a browser wants. - It never passes a time through
new Date(). Every clock in an answer is a wall clock at the place; the widgets slice the string, so a reader in another zone sees the same sunrise. - It never scores. A horoscope or a reading is text; nothing turns it into a percentage, a star rating or a lucky number.
- It never inserts answer text as markup. The only markup it inserts is the chart SVG the API drew.
MIT licensed. Source, issues and pull requests: kaaljyoti-integrations.