How to write a calculator in HTML and CSS without JavaScript

development material calculator on CSS and HTML, without a JS file, script tag and event handlers in HTML, we share by the start of the course on Full stack development in Python. For details, we invite under cat.

Formulation of the problem

In projects, CSS is often compiled into regular static HTML and CSS HAML and SCSS. The latter are used in many crazy projects, but I decided to keep mine simple: take a look at the whole the code.

How to do it?

Let’s start with user interaction. How to understand without JS that the button is pressed? Answer: using radio button values:

<input type="radio" name="x" id="q-1" /> 
<input type="radio" name="x" id="q-2" /> 
<label for="q-1">Quote 1</label>
<label for="q-2">Quote 2</label>

<p class="quote-1">...</p>
<p class="quote-2">...</p>


input, p { display: none }

#q-1:checked ~ .quote-1 { display: block; }
#q-2:checked ~ .quote-2 { display: block; }

give the result:

The labels are connected to the input so that clicking on them becomes clicking on the input. Labels simplify styling, so they’re better than clicking the radio button directly.

The ~ character is a selector that selects elements at the same level of complexity: A ~ B matches B elements after A. The code above hides the p elements by default, showing them only when the connected radio button is selected.

CSS variables and counters

The counter values ​​in the calc function are not applicable, so to generate a number, we declare a variable. Let’s write the property name with two hyphens (–) at the beginning and any CSS value: –colour: brown or –digit: 3. To substitute a variable, call the CSS function var.

CSS counters store and display numbers. They are used, for example, for automatic section numbering.

<input type="radio" name="theFirstDigit" id="set-to-1" /> 
<input type="radio" name="theFirstDigit" id="set-to-2" /> 
<input type="radio" name="theFirstDigit" id="set-to-3" /> 
<!-- insert labels -->

<div class="number-dsplay"></div>


#set-to-1:checked ~ div { --digit: 1; }
#set-to-2:checked ~ div { --digit: 2; }
#set-to-3:checked ~ div { --digit: 3; }

.number-display { counter-increment: digit var(--digit);  }
.number-display::after { content: counter(digit) }

give the result:

When the user checks the button, the –digit variable inside the div is set, which is inherited by all child elements. This value can’t be output directly, so let’s increment the digit counter and display it via the generated content.

To get numbers greater than 9, you just need to duplicate the existing numbers. Due to the carefully thought out structure of HTML and the use of intermediate variables, CSS duplication is minimized:

!-- digit inputs name="theFirstDigit -->

<div class="first-digit">
  <!-- digit inputs name="theSecondDigit" -->

  <div class="second-digit">
    <!-- ..and so on -->
/* Include previous CSS */

.first-digit { --first-digit: var(--digit); }
.second-digit { --second-digit: var(--digit); }

The –digit variable is still set via input, each individual div takes that value and assigns it to –first-digit, –second-digit and so on: no need to repeat the #set-to-1:checked code for each digit .

CSS Calc Function

The calc function in CSS performs calculations and is used, for example, when a width value is given: calc(100% – 95px). Let’s use calc to determine the number of the input element, as well as the result of all calculations:

[name="theFirstDigit"]:checked ~ * .set-number { --number: var(--first-digit); }
[name="theSecondDigit"]:checked ~ * .set-number {  
  --number: calc(var(--first-digit)*10 + var(--second-digit)); 
[name="theThirdDigit"]:checked ~ * .set-number {  
  --number: calc(var(--first-digit)*100 + var(--second-digit)*10 + var(--third-digit)); 
/* and so on */

The * selector selects all elements, so in the code above you will find .set-number – a child of any element after input with a flag and a specific name. The second selector overrides the first simply because it is placed after the first.

By adding several inputs to select an operation, we will get the final answer in a similar way. In this case, the values ​​are simply captured in the counter and displayed. The content property can also take a string, representing the operation of the calculator.

@property and @counter-style

Now you can create a calculator. But here’s the catch: there are only integers in the counters, we don’t have decimal places, so we need to divide the number into integer and fractional parts and find a way to round numbers.

In this case, counters cannot be used, because they cannot be entered in calc. Let’s use the experimental @property function: it defines a variable with functionality such as type checking or value inheritance control. Let’s define @property:

@property --integer {
    syntax: '<integer>';
    initial-value: 0;
    inherits: true;

So any value assigned to –integer is rounded up to an integer. To display a number up to seven decimal places, first perform the following calculations. Here –number is defined by external code:

.number-display {
    --abs-number: max(var(--number), -1 * var(--number)); 
    /* By suptracting 0.5 we make sure that we round down */
    --integer: calc(var(--abs-number) - 0.5);
    --decimal: calc((var(--integer) - var(--abs-number)) * 10000000);

    --sign-number: calc(var( --abs-number) / var(--number));

By using –integer for integers and –decimal for decimals, you can increment counters with similar names, but you can’t display them directly: for example, for the number 1.005, –integer is 1 and –decimal is 5.

The decimal places are padded with the @counter-style custom property, which is used to display a minus sign: we can’t tell the system that -0.5 is “negative zero”. Here’s how to properly display -0.5:

@counter-style pad-7 {
    system: numeric;
    symbols: "0" "1" "2" "3" "4" "5" "6" "7" "8" "9";
    pad: 7 "0"

@counter-style sign {
    system: numeric;
    symbols: "" "";

.number-display::after {
    content: counter(sign-number, sign) counter(integer) "." counter(decimal, pad-7);

The second argument to the counter function is the style. The pad-7 style defines the normal number system, except that any value with less than seven digits is padded with zeros.

The sign style also uses a number system, but we defined the characters as empty so it only displays a minus sign if needed.


All these are the key elements of the calculator, but there is still something left – this is stylization. You may have noticed that for each digit of the number in the current configuration there is a separate set of several inputs.

To always show the mark of the next digit, you can use the ~ selector, :checked and the display property, and split the content into separate elements, thus showing the decimal part only when necessary.

Theoretically, through the generation of HTML, you can get closer to a scientific calculator. For example, one can take advantage of the symmetry and periodicity of trigonometric functions by applying approximate calculations.

The hardest part is the parentheses, because we don’t know how to dynamically add them to calc, so we would have to have separate selectors and CSS for each scenario.


I wrote this calculator just for fun, but I learned a lot, it was a lot of fun, so if you have an idea for a trifling project, implement it. Why not?

In the meantime, we will help you improve your skills or master a profession that is in demand at any time from the very beginning:

Choose another in-demand profession.

Brief catalog of courses and professions

Data Science and Machine Learning

Python, web development

Mobile development

Java and C#

From basics to depth

As well as

Similar Posts

Leave a Reply