I made this in my spare time with apologies to the creators of HyperCard, to see how it would be on the modern web.

A HyperCard stack is like a pack of note cards that can be flipped through but stay in order. You can add or remove cards from your stack.

Each card can have button and field parts; to edit them, choose the appropriate tool, then drag and resize them. You make new ones by dragging a box with the command key.

HyperCard stacks were neat because they autosaved your work in the background. You can if you want.
Navigate through the stack with the buttons on the left, or type arrow keys. Create a new card with [+].

The authoring tools are above the stack. To make a Button Part or a Field Part, switch to the appropriate tool.

Command-drag to create a new Part, then drag it around the card (resize it by dragging the lower right corner). Double-click the Part to change its properties.

In any tool, you can hold down Command and Option together to show the outlines of all parts, and click to edit.
You can access the Script Editor by pressing in the Information dialog, or holding down Shift while you double-click or Command-Option-click.

Scripts catch messages sent by the user, and can perform actions such as beeping or going to the next card, in message handlers like this one:

on mouseUp
  beep
  go next card
end mouseUp
Username
Password

Create new stack...
Share this stack...
Open “New Stack”
Open “New Stack 2”
Account...
Log Out...
 🍪 

Wildcard script—> JavaScript
on <handler> <parameterList>function <handler>(<parameterList>) {
    [ <command> | <statement> ]*
end <handler> }
<command>
if <expression> thenif (<expression>) {
else} else {
end if}
return <expression>return <expression>;
<statement>
get <expression>_.it = <expression>;
put <expression> into <variable>
| set <variable> to <expression>
_.<variable> = <expression>;
answer <expression>alert(<expression>);
ask <expression> with <expression>prompt(<expression>,<expression>);
ask <expression>confirm(<expression>);
set [the] <property> of <part> to <expression><part>.<property> = <expression>;
send <message> <expressionList> to <part>Script.Send(<part>,<message>,<expressionList>);
<message> <expressionList>(this.<message>)(<expressionList>);
<expression>
<expression> or <expression>boolean(<expression>) || boolean(<expression>)
<expression> and <expression>boolean(<expression>) && boolean(<expression>)
<expression> [ is | equals | = ]! <expression><expression> == <expression>
| <expression> [ is not | ≠ | <> ]! <expression><expression> != <expression>
<expression> [ < | is less than ]! <expression>number(<expression>) < number(<expression>)
| <expression> [ > | is more than ]! <expression>number(<expression>) > number(<expression>)
| <expression> [ <= | is less [than] or equal to ]! <expression>number(<expression>) <= number(<expression>)
| <expression> [ >= | is more [than] or equal to ]! <expression>number(<expression>) >= number(<expression>)
<expression> & <expression>Array(<expression>,<expression>).join('')
| <expression> && <expression>Array(<expression>,<expression>).join(' ')
<expression> [ plus | + ]! <expression>number(<expression>)+number(<expression>)
| <expression> [ minus | - ]! <expression>number(<expression>)-number(<expression>)
<expression> [ times | * ]! <expression>number(<expression>)*number(<expression>)
| <expression> [ divided by | / ]! <expression>number(<expression>)/number(<expression>)
| <expression> div <expression>Math.floor(number(<expression>)/number(<expression>))
| <expression> mod <expression>number(<expression>) % number(<expression>)
<factor>^<expression>Math.pow(<factor>, <expression>)
<factor>
<factor>
<number> | <string> | true | false
<message>(<expressionList>)this.<message>(<expressionList>)
[the] <identifier> of <part><part>.<identifier>
[the] <message> of <factor>this.<message>(<factor>)
the <identifier>wildcard.<identifier>
my <identifier>this.me.<identifier>
methis.me
comma','
return'\n'
space' '
empty''
-<factor>
not <factor>!boolean(<factor>)
<variable>_.<variable>
(<expression>)(<expression>)
<part>
wildcard
methis.me
this stack(this.me.closest('stack-part') || currentStack())
this card(this.me.closest('card-part') || currentStack().querySelector('card-part.current'))
Where
<expressionList> = [ <expression> [, <expression> ]* ]
<parameterList> = [ <parameter> [, <parameter> ]* ]
<handler> | <message> | <variable> | <property> = <identifier>
<identifier> /[A-Z][A-Z\d]*/i
<number> /[\d]+([.][\d]+)?/
<string> /"[^"]*"/
Wildcard script
-- these handlers are available to all Wildcard scripts. -- When WildCard gets the message called beep, this handler acts -- To send the 'beep' message, you type 'beep' in the message box on beep perform_system_beep(); end beep -- Handlers can accept parameters and return a value on double n return n plus n end double -- You can use JavaScript on sqrt n return Math.sqrt(n); end sqrt on random n return 1+Math.floor(Math.random()*n); end random -- Send messages from Javascript with wildcard.do('beep') or just wildcard.Beep(); on do lineOfScript put "on run" & return & lineOfScript & return & "end run" into handler return new Script(null, _.handler).run(); end do
Written and published ©2021 Hypervariety Custom Software. Thank you Bill Atkinson.