From the maker of: FlagMeister.github.io , ChessMeister.github.io
This project uses modern browser technologies. Open in a 'evergreen' browser
If you want to make it work in outdated browsers see the WebComponents polyfill
License: Unlicense: This is free software released into the public domain - https://choosealicense.com/licenses/unlicense/
The single file elements.cardmeister.min.js is:
-
SVG data for 52 playing cards - 500 KB painstakingly slimmed
-
one W3C Autonomous Custom Element :
<playing-card cid=Queen-of-Hearts> </playing-card>
-
52 customized Built-In IMG elements :
<img is=queen-of-hearts>
(not supported in Safari; no longer created since 2024) -
Total GZip file size: (under) 14 KB² creating 52 playingcards:
A 'Hello World!' with Framework X took hours installing tools and used 95 KiloBytes ... to display 12 characters.
Something did not feel right!
What happened to the days when all you needed were some HTML tags and a text-editor?
I learned to PEEK and POKE at the age of 10 on a TRS-80 and learned HTML (a bit late) at 25
The ability to 'peek at' and learn from someone else's effort was fantastic.
Alas in the past years 'Web Development' has become something For-Rocket-Scientists only
W3C standard Custom Elements¹ make writing semantic HTML as cool as it was in my early days .. without any Framework!
Playingcard(t)s are a good subject to demonstrate the power of a Custom Element
- one single file creates a
<playing-card>
Custom Element - (loads of) attributes for configuration
- 52 SVG playingcards
- no external SVG images
- All in less than 16 KB² because my first computer had 16 KiloBytes memory
Feel free to PEEK around, and if you want to POKE, submit an issue.
A special thanks to users Supersharp and Intervalia for their always helpful answers on StackOverflow!
-- Danny Engelman
-- Amsterdam 🌷🌷🌷 the Netherlands
-- Summer 2019 👴🏽 25 years after my first HTML page
-
The terms Custom Elements & Web Components are used interchangeably.
Web Components is the umbrella term for the three main techonologies:- Custom Elements - the Custom Elements API
- Shadow Dom (not used in this project)
- HTML Templates (not used in this project)
- some references include a fourth: ES Modules (not used in this project)
-
Minified 16 KB GZip
It is a card game .. I cheated ... but can you spot where? (explained in documentation below)
For an introduction to W3C standard Custom Elements/WebComponents read the excellent Blog by Danny Moerkerke
Like the HTML5 <video>
tag, the Custom Element <playing-card>
abstracts complex functionality into one HTML tag
💡 Custom Elements must be declared in a Namespace, so require a - hyphen in the tag name
<html>
<head>
<script src="elements.cardmeister.full.js"></script>
</head>
<body>
<playing-card rank="Queen" suit="Hearts"></playing-card>
</body>
</html>
(Autonomous) Custom Element <playing-card>
creates an image with the SVG as data:image
src
Saves you from headaches with SVG in a document (duplicate symbol ids, bleeding CSS etc.)
and makes it easy to add HTML Drag-Drop functionality.
💡 Autonomous Custom Elements require a closing tag: </playing-card>
52 Custom Elements are also created customizing the IMG element so you can use:
<img is="ten-of-hearts" />
<img is="jack-of-hearts" />
<img is="queen-of-hearts" />
<img is="king-of-hearts" />
<img is="ace-of-hearts" />
The Autonomous Custom Element
<playing-card>
and the Customized Built-In Element<img is=...>
are two different flavours of Custom Elements which (in this case) do the same.
You can pick the one that suits your application. Or use them both in one page: https://cardmeister.github.io
💡 requires a polyfill in Safari, because Apple does not want to implement this part of the spec.
💡 declaration must be all lowercase!
💡 the IMG element is self-closing by default, no closing tag required!
💡 the is=
declaration only takes effect when the DOM element is created
💡 on the (customized IMG) element you can use all attributes/properties documented below
👨💻 tech: Autonomous Elements can have ShadowDom, Customized Elements can not have ShadowDom
👨💻 tech: Because IMG elements don't contain text or have descendants, neither CSS selector img:before
or img:after
can be used
Since <playing-card>
only creates a single IMG no shadow-DOM is required/used, thus IMG can be styled with global CSS:
[rank="queen"] img {
border: 3px solid greenyellow;
transform: rotate(15deg);
}
or for the customized IMG element:
img[is*="queen"] {
border: 3px solid greenyellow;
transform: rotate(15deg);
}
I have the selection and drag/drop part nearly done: https://playing-cards.github.io/Solitaire/
<cardts-game>
<cardts-deck type="frenchdeck"></cardts-deck>
<cardts-pile>
<% 7 times %>
<cardts-pile type="sequence"></cardts-pile>
<%%>
</cardts-pile>
<cardts-pile id="foundation" type="stack">
<% 4 times %>
<cardts-pile></cardts-pile>
<%%>
</cardts-pile>
<cardts-game></cardts-game
></cardts-game>
What if cardts could:
- change suit during a game
- fade out during a game
- change from Queens to Kings
- ...
- ...
playingcards Terminology:
- 'court' is UK English for 'face' US English. So Jack, Queen and King are court-cards (courts)
- SHDC is short for Spades-Hearts-Diamonds-Clubs
- 0123 are Array indexes (SHDC)
- cid = card id
As = Ace of Spades , TD = Ten of Diamonds
💡 attributes can be written as attributes in HTML:<playing-card id=MyCard cid=Qh ></playing-card>
💡 attributes can be set as attribute:MyCard.setAttribute('cid','Kh')
💡 or as property: MyCard.cid='Kh'
(sets the attribute value)
See: https://cardmeister.github.io
cid
- Standard Card ID notation:cid=Qh
= Queen of Heartsletters
- Custom localized court letterscourts
- mix rank/courts imagessuits
- Mix suit/court imagessuitcolor
- Change SHDC suit colorrankcolor
- Change rank colorcardcolor
- card coloropacity
- set card content opacitycourtcolors
- Change court image colorsbordercolor
- set card border colorborderradius
- set card border radiusborderline
- set card border line thicknessbacktext
- card backside textbacktextcolor
- card backside colorsvg
- (undocumented) add attributes to main SVG definition (eg: svg="transform='rotate(5,5,5)'")pips
- (undocumented) custom suitsymbols (pips) on number cards
<playing-card cid=As></playing-card>
<playing-card cid=5d></playing-card>
<playing-card cid=Tc></playing-card>
<playing-card cid=Jh></playing-card>
<playing-card cid=Qc></playing-card>
💡 overrules rank=
and suit=
notation
💡 case insensitive: qC
is the same as: Qc
💡 10C
is processed as TC
💡 Queen-of-Clubs
is processed as Qc
💡 <img is=queen-of-clubs>
uses cid
internally - is=
requires lowercase full Element name!!
default: AJQK
Rename Ace, Jack, Queen, King letters AJQK to Dutch locale 'Aas Boer Vrouw Heer'
<playing-card suit=Spades rank=Ace letters=ABVH></playing-card>
<playing-card suit=Hearts rank=Jack letters=ABVH></playing-card>
<playing-card suit=Diamonds rank=Queen letters=ABVH></playing-card>
<playing-card suit=Clubs rank=King letters=ABVH></playing-card>
💡 a 4 letter string is used so it can be a global setting for all created cards
💡 Custom letters
draw all ranks (A,2,3,4 etc.) in Arial font
💡 letters
can not be Emoji (Unicode strings)
default: 012
Before anyone complains the Queen is always #2
Rearrange SHDC court images to KJQ = 201 :
<playing-card suit="Hearts" rank="Jack" courts="201"></playing-card>
<playing-card suit="Spades" rank="Queen" courts="201"></playing-card>
<playing-card suit="Diamonds" rank="King" courts="201"></playing-card>
default: #E55 (red)
<playing-card rank="0" backcolor="red"></playing-card>
<playing-card cid="00"></playing-card>
<playing-card
rank="0"
backcolor="#44F"
backtext="COPYRIGHT"
backtextcolor="black"
></playing-card>
default: #000,red,red,#000
(rankcolor added on request in 2024; can also be a single color value)
default: #FFF
<playing-card
rank="1"
suit="0"
suitcolor="#FFF,#FFF,#FFF,red"
cardcolor="red"
></playing-card>
<playing-card
rank="1"
suit="1"
suitcolor="#FFF,yellow,#FFF,red"
cardcolor="green"
></playing-card>
<playing-card
rank="1"
suit="2"
suitcolor="#FFF,#FFF,black,red"
cardcolor="dodgerblue"
></playing-card>
<playing-card
rank="1"
suit="3"
suitcolor="#FFF,#FFF,#FFF,red"
cardcolor="yellow"
></playing-card>
The string (all 4 settings in one string for global declaration!) is converted to an array so can be written as:
<playing-card rank="1" suit="0" suitcolor="#FFF," cardcolor="red"></playing-card>
<playing-card rank="1" suit="1" suitcolor=",yellow" cardcolor="green"></playing-card>
<playing-card rank="1" suit="2" suitcolor=",,black" cardcolor="dodgerblue"></playing-card>
<playing-card rank="1" suit="3" suitcolor=",,,red" cardcolor="yellow"></playing-card>
💡 You can change one card with: element.suitcolor='blue';
💡 non-color values will break the SVG suit display
💡 add attribute norank=true
and only the big center suit remains
default: 0.8
💡 0.8 makes the cards look not too sharp, and you can highlight playing-cards by setting opacity to 1
<playing-card rank="1" suit="0" opacity="1"></playing-card>
<playing-card rank="1" suit="1" opacity=".75"></playing-card>
<playing-card rank="1" suit="2" opacity=".5"></playing-card>
<playing-card rank="1" suit="3" opacity=".1"></playing-card>
💡 CSS opacity makes the whole IMG transparent! <playing-card opacity=n>
leaves the <playing-card>
backcolor at 100%
default: #444 (darkgray)
default: 12
default: 1
<playing-card
rank="1"
suit="0"
bordercolor="red"
borderradius="12"
borderline="12"
></playing-card>
<playing-card
rank="4"
suit="0"
bordercolor="hotpink"
borderradius="100%"
borderline="20"
></playing-card>
<playing-card
rank="13"
suit="H"
bordercolor="gold"
borderradius="12"
borderline="38"
></playing-card>
this setting will most likely be gone in 'less SVG' version 2
default: #DB3,red,#44F,#000,#000,4 (gold,red,blue,black,blacklines,linethickness)
<playing-card
suit="Hearts"
rank="Jack"
courtcolors="gold,red,blue,black,black,4"
></playing-card>
<playing-card
suit="Spades"
rank="Queen"
courtcolors="#DB3,lightcoral,lightblue,slategray,#000,1"
></playing-card>
<playing-card
suit="Diamonds"
rank="King"
courtcolors="gold,red,green,orange,#000,4"
></playing-card>
💡 suit decorations are set by the suitcolor
attribute
All good open-source SVG playingcards available are high-precision ready for print.
In the HTML5 deck of cards the single King of Hearts 1_13.svg card is 69 KB
- I started with the 550 KB for 52 CC-0 licensed cards from the card generator by Adrian Kennard
- reduced precision with: Jake Archibalds GUI for SVGO
(this causes some alignment 'errors' you only see viewing a court card full screen) - spent some time in Inkscape reducing details you don't see on a computer screen
- cut everything up in separate SVG paths
- made redundant paths into single JS functions/variables
- (see below) cleaned up the Jack of Spades court which (since ages?) had the suit on the wrong? side
- wrote a
SVGcardt()
function to create an IMG with SVG data - Re-drawing the Heart pips on the Jack, I saw an opportunity to cheat and save 70% of SVG data (see FAQ)
The SVG can be reduced more using a LZMA compressor
But HTTP requires Base64 encoding.
And the Browser needs to decompress the data which takes a hefty 200ms (and a 6.9KB decoder)
The first PoC did LZMA de-compression and lazy loaded all 12 court images.
Since GZip is a similar LZ compression technique (over the whole file) there is only a gain on slow 3G connections
A Custom Element requires its own Namespace, so you are stuck to something with a - hyphen.
You can create 52 Autonomous Custom Elements (extend from <playing-card>
):
<queen-of-hearts></queen-of-hearts>
<ten-of-spades></ten-of-spades>
...
But there is little value as you can not use partial Selectors for tag names.
To select all Spades you still need attributes:
<queen-of-hearts hearts></queen-of-hearts>
<ten-of-spades spades></ten-of-spades>
...
Because queen-of-hearts
can either be a 'Autonomous Custom Element' OR a 'Customized Built-In Element'
<playing-card>
creates 52 Customized IMG elements:
<img is="ten-of-hearts" />
<img is="jack-of-hearts" />
<img is="queen-of-hearts" />
<img is="king-of-hearts" />
<img is="ace-of-hearts" />
💡 declaration must be all lowercase!
💡 the IMG element is self-closing by default, no closing tag required!
💡 the is=
declaration only takes effect when the DOM element is created
💡 on the (customized IMG) element you can use all attributes/properties documented above
Yes, an option is to exclude the (compressed/raw) SVG data from the Element file.
The Element can then fetch the data asynchronous.
This project was about stuffing everything into one file.
If you need an installer to copy one file: elements.cardmeister.min.js
You might find a better career flipping burgers at McDonalds.
Read: I stuck the UNlicense on this code. You can do whatever you want with the code.
I don't want to be responsible for maintaining a dependency for others
You do NOT need the SVGcardt()
Source Code to use <playing-card>
or <img is=..>
in applications
The <playing-card>
Custom Element declaration in elements.cardmeister.min.js is unlicensed.
You can rename <playing-card>
to anything you want and customize the Custom Element to your liking.
The <img is=..>
declaration is part of the minified SVGcardt() code.
The SVG creation code took some blood, sweat and tears and is closed source for now.
It needs some refactoring and documentation before this is public ready.
Watch this GitHub repo and you will know when it becomes available.
See F12 Network tab, data:image/svg
cardts take 0 milliseconds download time
Note: FireFox is noticeably slower in drawing the cards.
Creating the card does take CPU time. Maybe a WebWorker can improve performance (but I can't stick JScode and a WebWorker in one single file)
😉 Causing havoc in Gotham City?!
I haven't found a good Joker Card design yet, if you have one let me know
I wanted to learn how far I could go with vanilla JavaScript
Then I wanted to proof you don't need libraries/frameworks
Look closely at the court cards....
The 16 KB version uses the same (JQK Hearts) court image with slightly different colors to distract your eye.
default: 0123 (defaults to 1111 in the 16 KB version!)
Change Spades=0, Hearts=1, Diamonds=2, Clubs=3 court image:
<playing-card suit=S rank=Queen suits=1111></playing-card>
<playing-card suit=H rank=Queen suits=1111></playing-card>
<playing-card suit=D rank=Queen suits=1111></playing-card>
<playing-card suit=C rank=Queen suits=1111></playing-card>
The Full Version elements.cardmeister.full.js with 12 different court images adds 110 KB raw SVG data
Making the single file 60 KB GZipped!
See: index.html?#full - 📄 source
Apart from the court images there are no differences.
It was fun (and took some time) breaking that 16 KB barrier, and helped making the full version smaller as well.
Use the Full version !
The Min version can be used for slow/low-bandwidth applications.
Create FreeCell with HTML Custom Elements
inspiration: https://www.free-freecell-solitaire.com/freecell.html
(something like) This HTML should create ALL game functionality
<cardts-game>
<cardts-pile id="freecells" type="any">
<% 4 times %>
<cardts-pile max="1"></cardts-pile>
<%%>
</cardts-pile>
<cardts-pile id="foundation" type="stack">
<% 4 times %>
<cardts-pile></cardts-pile>
<%%>
</cardts-pile>
<cardts-pile>
<% 8 times %>
<cardts-pile type="sequence"></cardts-pile>
<%%>
</cardts-pile>
<cardts-deck type="frenchdeck"></cardts-deck>
<cardts-game></cardts-game
></cardts-game>
<playing-card cid="F0"></playing-card>
<playing-card cid="FH"></playing-card>
<playing-card cid="F2"></playing-card>
<playing-card cid="FC"></playing-card>
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to http://unlicense.org
https://www.youtube.com/watch?v=7hx4gdlfamo&t=1s
or the Muppets version: https://www.youtube.com/watch?v=kNnrTNFWcsg
- https://www.dannymoerkerke.com/blog/web-components-will-replace-your-frontend-framework
- https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements
- Custom Elements/Web Components best practices
https://developers.google.com/web/fundamentals/web-components/best-practices - Andrea Giammarchi/WebReflection - Why I use WebComponents
https://gist.github.com/WebReflection/71aed0c811e2e88e3cd3c647213f0e6c
-
Comparison of multiple solutions: https://socialcompare.com/en/comparison/css-playing-cards-pgnq7dx
-
SVG (server side) Card Creator (I used these and altered them)
https://www.me.uk/cards/makeadeck.cgi -
ES6/Polymer WebComponent - separate SVG files for courts (last commit:2017)
https://www.webcomponents.org/element/vpusher/game-card -
CSS playing cards - PNG images (over 250 Kb) (last commit: 2012)
https://donpark.github.io/scalable-css-playing-cards/ -
HTML5 deck of cards (all 52 cards are separate SVG files) (last commit: 2017)
https://github.com/pakastin/deck-of-cards
https://deck-of-cards.js.org/
King of Hearts alone is 69 KB : https://deck-of-cards.js.org/faces/1_13.svg -
CSS only (no Court images) (last commit: 2012)
https://github.com/zachwaugh/Helveticards -
PNG card background image = https://bfa.github.io/solitaire-js/img/card_back_bg.png
-
DeckOfCards API - http://deckofcardsapi.com/
-
Solitaire
-
Redux,React, the lot https://codepen.io/HunorMarton/details/rwpGXj
-
(2017) No frameworks/library No drag drop, PNG images for courts https://bfa.github.io/solitaire-js/
https://github.com/bfa/solitaire-js
-
-
Poker API - https://rapidapi.com/danielamitay/api/poker-odds
- SVGO GUI - https://jakearchibald.github.io/svgomg/
- http://www.petercollingridge.appspot.com/svg-editor
- https://tympanus.net/codrops/2019/01/15/svg-filters-101/
- SVG minification and GZIP - https://blog.usejournal.com/of-svg-minification-and-gzip-21cd26a5d007
- https://css-tricks.com/gotchas-on-getting-svg-into-production/
- https://stackoverflow.com/questions/18467982/are-svg-parameters-such-as-xmlns-and-version-needed
- https://codepen.io/tigt/post/optimizing-svgs-in-data-uris
- http://lzma-js.github.io/LZMA-JS/demos/advanced_demo.html
- https://github.com/LZMA-JS/LZMA-JS
- https://dafrok.github.io/gzip-size-online/
- Analyze GZ compression:
https://encode.ru/threads/1889-gzthermal-pseudo-thermal-view-of-Gzip-Deflate-compression-efficiency
Yes, I did https://chessmeister.github.io
<img is=white-queen at=D5>
- https://commons.wikimedia.org/wiki/Category:SVG_chess_pieces/Maurizio_Monge
- https://codepen.io/jeansarlon/pen/WpZNda/
- https://ilhooq.github.io/svgchessboard/demo/
- https://rexxars.github.io/react-chess/
Load SVG content in main document:
<iframe src="file.svg" onload="this.before(this.contentDocument.children[0]); this.remove();"></iframe>
Published: 2020-08-06 17:13