Lines thick and blurry HTML canvas

I am drawing a simple grid, like so:

const canvas = document.getElementById('gridCanvas') as HTMLCanvasElement;
const context = canvas.getContext('2d') as CanvasRenderingContext2D;

// Define the size of each square
const squareSize = 20; // NxN squares

// Define the number of squares in each direction
const numRows = 10; // X
const numCols = 15; // Y

// Set the canvas dimensions
canvas.width = numCols * squareSize;
canvas.height = numRows * squareSize;

// Function to draw the grid
function drawGrid() {
    context.clearRect(0, 0, canvas.width, canvas.height);

    context.strokeStyle = 'black';
    context.lineWidth = 1; // Set the line width to 1 pixel
    context.globalAlpha = 1.0; // Ensure full opacity

    // Draw the horizontal lines
    for (let row = 0; row <= numRows; row++) {
        context.beginPath();
        context.moveTo(0, row * squareSize);
        context.lineTo(canvas.width, row * squareSize);
        context.stroke();
    }

    // Draw the vertical lines
    for (let col = 0; col <= numCols; col++) {
        context.beginPath();
        context.moveTo(col * squareSize, 0);
        context.lineTo(col * squareSize, canvas.height);
        context.stroke();
    }
}

// Draw the grid when the page loads
window.onload = () => {
    drawGrid() 
};

here’s the HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas Grid</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        canvas {
            /* border: 0px solid black; */
        }
    </style>
</head>
<body>
    <canvas id="gridCanvas"></canvas>
    <script src="../dist/main.js"></script>
</body>
</html>

const canvas = document.getElementById('gridCanvas');
const context = canvas.getContext('2d');

// Define the size of each square
const squareSize = 20; // NxN squares

// Define the number of squares in each direction
const numRows = 10; // X
const numCols = 15; // Y

// Set the canvas dimensions
canvas.width = numCols * squareSize;
canvas.height = numRows * squareSize;

// Function to draw the grid
function drawGrid() {
  context.clearRect(0, 0, canvas.width, canvas.height);

  context.strokeStyle = 'black';
  context.lineWidth = 1; // Set the line width to 1 pixel
  context.globalAlpha = 1.0; // Ensure full opacity

  // Draw the horizontal lines
  for (let row = 0; row <= numRows; row++) {
    context.beginPath();
    context.moveTo(0, row * squareSize);
    context.lineTo(canvas.width, row * squareSize);
    context.stroke();
  }

  // Draw the vertical lines
  for (let col = 0; col <= numCols; col++) {
    context.beginPath();
    context.moveTo(col * squareSize, 0);
    context.lineTo(col * squareSize, canvas.height);
    context.stroke();
  }
}

// Draw the grid when the page loads
window.onload = () => {
  drawGrid()
};
body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  margin: 0;
}

canvas {
  /* border: 0px solid black; */
}
<canvas id="gridCanvas"></canvas>

But the lines it is drawing are 6 pixels thick, completely translucent, blurry, and not black. Here’s a screenshot with some of it zoomed in.

enter image description here

When I set the line thickness to 0.5 instead of 1, the lines are still 6 pixels thick, just lighter.

My device pixel ratio is 2, but I don’t understand then why the lines are 6px thick and not 2px. I ultimately want the lines to be 1 physical pixel thick on all screens.

I have tried adding the following code:

// Set the canvas dimensions in CSS pixels
canvas.style.width = `${numCols * squareSize}px`;
canvas.style.height = `${numRows * squareSize}px`;

// Set the canvas dimensions in device pixels
canvas.width = numCols * squareSize * devicePixelRatio;
canvas.height = numRows * squareSize * devicePixelRatio;

// Scale the drawing context to match the device pixel ratio
context.scale(devicePixelRatio, devicePixelRatio);

But the lines are still 2px thick, and translucent, even when setting lineWidth to 0.5.

enter image description here