diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..0c3f5873e --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,69 @@ +name: lint + +on: + pull_request: + types: [opened, synchronize, reopened] + branches: + - main + +concurrency: + group: lint-${{ github.ref }} + cancel-in-progress: true + +jobs: + html-css: + name: htmlhint + stylelint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install npm dependencies + run: npm ci + + - name: Lint HTML (htmlhint) + run: npm run lint:html + + - name: Lint CSS (stylelint) + run: npm run lint:css + + a11y: + name: pa11y-ci (WCAG2AA) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install npm dependencies + run: npm ci + + - name: Build Jekyll site + run: bundle exec jekyll build + + - name: Run pa11y-ci against built site + run: npm run lint:a11y:ci + + - name: Upload pa11y report (always) + if: always() + uses: actions/upload-artifact@v4 + with: + name: pa11y-report + path: pa11y-report.json + if-no-files-found: ignore diff --git a/.github/workflows/visual-qa.yml b/.github/workflows/visual-qa.yml new file mode 100644 index 000000000..aca292cc7 --- /dev/null +++ b/.github/workflows/visual-qa.yml @@ -0,0 +1,66 @@ +name: visual-qa + +on: + pull_request: + types: [opened, synchronize, reopened] + branches: + - main + +concurrency: + group: visual-qa-${{ github.ref }} + cancel-in-progress: true + +jobs: + playwright: + name: Playwright (visual + functional QA) + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install npm dependencies + run: npm ci + + - name: Build Jekyll site + run: bundle exec jekyll build + + # Playwright browsers are intentionally NOT installed via a + # postinstall hook so contributors running only lint never pay + # the download cost. We install on demand here. + - name: Install Playwright browsers + run: npx playwright install --with-deps chromium firefox + + - name: Run Playwright suite + run: npm run test:e2e:ci + env: + CI: 'true' + + - name: Upload Playwright HTML report + if: always() + uses: actions/upload-artifact@v4 + with: + name: playwright-report + path: playwright-report/ + if-no-files-found: ignore + retention-days: 14 + + - name: Upload Playwright raw results (traces, videos, screenshots) + if: failure() + uses: actions/upload-artifact@v4 + with: + name: playwright-results + path: test-results/ + if-no-files-found: ignore + retention-days: 14 diff --git a/.gitignore b/.gitignore index eb916789c..b7e9f6606 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,12 @@ _site vendor rdfa/node_modules rdfa/package-lock.json +node_modules +.cache +pa11y-report.json + +# Playwright — keep baseline PNGs, ignore only failure/diff artefacts +test-results/ +playwright-report/ +**/*-actual-*.png +**/*-diff-*.png diff --git a/.htmlhintrc b/.htmlhintrc new file mode 100644 index 000000000..6d7367ba2 --- /dev/null +++ b/.htmlhintrc @@ -0,0 +1,22 @@ +{ + "tagname-lowercase": true, + "attr-lowercase": true, + "attr-value-double-quotes": true, + "attr-no-duplication": true, + "doctype-first": false, + "tag-pair": true, + "tag-self-close": false, + "spec-char-escape": true, + "id-unique": true, + "src-not-empty": true, + "title-require": true, + "alt-require": true, + "doctype-html5": true, + "inline-style-disabled": false, + "inline-script-disabled": false, + "space-tab-mixed-disabled": "space", + "id-class-ad-disabled": false, + "href-abs-or-rel": false, + "attr-unsafe-chars": true, + "head-script-disabled": false +} diff --git a/.pa11yci b/.pa11yci new file mode 100644 index 000000000..5cf20a73d --- /dev/null +++ b/.pa11yci @@ -0,0 +1,21 @@ +{ + "defaults": { + "standard": "WCAG2AA", + "runners": ["axe"], + "timeout": 30000, + "wait": 500, + "threshold": 0, + "chromeLaunchConfig": { + "args": ["--no-sandbox", "--disable-dev-shm-usage"] + }, + "ignore": [] + }, + "urls": [ + "http://127.0.0.1:4000/", + "http://127.0.0.1:4000/apps", + "http://127.0.0.1:4000/get_a_pod", + "http://127.0.0.1:4000/for_developers", + "http://127.0.0.1:4000/for_users", + "http://127.0.0.1:4000/community" + ] +} diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 000000000..ad1d007c3 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,37 @@ +{ + "extends": ["stylelint-config-standard"], + "rules": { + "no-descending-specificity": null, + "no-duplicate-selectors": null, + "declaration-block-no-redundant-longhand-properties": null, + "declaration-block-no-shorthand-property-overrides": null, + "selector-class-pattern": null, + "selector-id-pattern": null, + "custom-property-pattern": null, + "keyframes-name-pattern": null, + "value-keyword-case": null, + "hue-degree-notation": null, + "color-function-notation": null, + "alpha-value-notation": null, + "shorthand-property-no-redundant-values": null, + "declaration-empty-line-before": null, + "rule-empty-line-before": null, + "comment-empty-line-before": null, + "length-zero-no-unit": null, + "number-max-precision": null, + "font-family-name-quotes": null, + "font-family-no-missing-generic-family-keyword": null, + "media-feature-range-notation": null, + "at-rule-empty-line-before": null, + "color-hex-length": null, + "property-no-vendor-prefix": null, + "value-no-vendor-prefix": null, + "selector-no-vendor-prefix": null, + "at-rule-no-vendor-prefix": null, + "import-notation": null, + "no-empty-source": null, + "color-function-alias-notation": null, + "color-function-notation": null + }, + "ignoreFiles": ["assets/css/styles.css"] +} diff --git a/FAQ.html b/FAQ.html index b64a2df2c..bcdd5aaf3 100644 --- a/FAQ.html +++ b/FAQ.html @@ -7,7 +7,7 @@ ---
-

Got a question that’s not here? Try asking on the Solid forum, on the Matrix Solid Project room or using the Solid tag on Stack Overflow.

+

Got a question that’s not here? Try asking on the Solid forum, on the Matrix Solid Project room or using the Solid tag on Stack Overflow.

@@ -18,17 +18,17 @@

What exactly is personal data?

More details...

Examples of personal data include: location, genome data, written communication, spoken communication, lists of contacts, internet browsing habits, financial transactions, supermarket spending, tax payments, criminal record, laptop and mobile phone camera lens recording, device microphone recordings, driving habits via car trackers, mobile and health records, fitness activity, nutrition, substance use, heartbeat, sleep patterns and other vital signs.

-

In Europe personal data is defined under the General Data Protection Regulation (GDPR) as "any information relating to an identified or identifiable natural person (‘data subject’); an identifiable natural person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number, location data, an online identifier or to one or more factors specific to the physical,physiological, genetic, mental, economic, cultural or social identity of that natural person;"

+

In Europe personal data is defined under the General Data Protection Regulation (GDPR) as "any information relating to an identified or identifiable natural person (‘data subject’); an identifiable natural person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number, location data, an online identifier or to one or more factors specific to the physical,physiological, genetic, mental, economic, cultural or social identity of that natural person;"

What is interoperability?

-

According to the Cambridge dictionary, interoperability is “the quality of being able to be used together”. For example, if you use a Solid app, you can store your data in any Pod because apps and Pods are interoperable. Also, you should be able to reuse data from one Solid calendar app to another because apps are interoperable.

+

According to the Cambridge dictionary, interoperability is “the quality of being able to be used together”. For example, if you use a Solid app, you can store your data in any Pod because apps and Pods are interoperable. Also, you should be able to reuse data from one Solid calendar app to another because apps are interoperable.

What is a Pod?

-

A Pod is where data is stored on the Web with Solid. (see quote). A user may store their data in one Pod or several Pods, and applications read and write data into the Pod depending on the authorisations granted by the user or users associated to that Pod.

+

A Pod is where data is stored on the Web with Solid. (see quote). A user may store their data in one Pod or several Pods, and applications read and write data into the Pod depending on the authorisations granted by the user or users associated to that Pod.

What is a WebID?

A WebID is a unique identifier used to identify a specific user. An example of what a WebID could look like is: https://fulano.pod.provider/profile/card#me. To share data with a third party, a user associates sharing preferences to the WebID of that third party. Don’t worry though: it’s not just one more login to remember.

More details... -

A WebID is an Internationalised Resource Identifier (IRI) that can be dereferenced as a FOAF profile document serialized in RDF (source). In Solid, WebIDs are used to identify Agents i.e. people and organisations as well as to manage their access rights though Web Access Control.

+

A WebID is an Internationalised Resource Identifier (IRI) that can be dereferenced as a FOAF profile document serialized in RDF (source). In Solid, WebIDs are used to identify Agents i.e. people and organisations as well as to manage their access rights though Web Access Control.

@@ -44,7 +44,7 @@

How are Pods differe

To log into Solid apps and/or access data on Pods, you just have to provide your WebID and login to your Identity Provider, like "Sign in with Google" today. It is possible for the same company or organisation to be both a Pod Provider and a Identity Provider although they are distinct separable services that are compatible with other Pod Providers and Identity Providers (which means you are free to choose the provider that suits you the most).

More details... -

An Identity Provider implements an identification protocol (e.g. OIDC), and allows you to prove that you own the WebID.

+

An Identity Provider implements an identification protocol (e.g. OIDC), and allows you to prove that you own the WebID.

A Pod Provider delivers storage space under one or more domains, usually (but not necessarily) pointed to by <webID> solid:storage <pod> statements included in the profile document associated to the WebID.

@@ -54,7 +54,7 @@

How are Pods differe

Web Standards

How does Solid relate to other Web standards?

-

Solid does not reinvent the wheel. When you use Solid you will still be able to access your data using the same Web browser on the same computer. Solid is still the web, but with a few things added and a few assumptions overturned (see quote at 33:30).

+

Solid does not reinvent the wheel. When you use Solid you will still be able to access your data using the same Web browser on the same computer. Solid is still the web, but with a few things added and a few assumptions overturned (see quote at 33:30).

Solid adds:

More details -

Solid is built on top of existing Web standards. The core Solid specification relies on LDP and WAC (WAC draft, both being based on HTTP and RDF vocabularies. Solid also uses a subset of SPARQL UPDATE through HTTP PATCH queries. Identification in Solid is based on WebID-TLS and/or OIDC.

+

Solid is built on top of existing Web standards. The core Solid specification relies on LDP and WAC (WAC draft, both being based on HTTP and RDF vocabularies. Solid also uses a subset of SPARQL UPDATE through HTTP PATCH queries. Identification in Solid is based on WebID-TLS and/or OIDC.

@@ -86,14 +86,14 @@

Are S

No. Self-hosting means that your data would sit at home on your own physical hard drive or server. Self-hosting is possible but not essential when using Solid, and it currently requires some technical knowledge. A Solid user can rely on an Identity Provider and a Pod Provider, and is not expected to have any particular expertise. It is also possible to self-host and become a small Identity Provider and Pod Provider, to provide Solid to your family, association, friends…

More details -

The Solid standard is open as are some of its implementations such as the Community Solid Server meaning that anyone can self-host their own Identity and Pod or the Identity and Pod of a group of users.

+

The Solid standard is open as are some of its implementations such as the Community Solid Server meaning that anyone can self-host their own Identity and Pod or the Identity and Pod of a group of users.

Can data exist in more than one Pod?

Yes, the same data point can exist in more than one Pod. You can choose to have more than one Pod deliberately, for example for work and for home, in which case, some data may be relevant to both settings. You can also choose to have a single Pod and give selective access. Some data is relevant to multiple users so may be replicated in a slightly different way in another person’s Pod. For example, if you have a conversation with someone the data may be stored in both your Pod and the Pod of the person you spoke with.

Is it possible to use Solid offline (at least partially)?

Eventually, yes. The Solid long term vision includes local first and a flexibility of different topologies of patch-passing sync networks. However, there are no implementations yet.

When I want to leave a Pod provider, can I take my data with me?

-

Yes, unless the Pod provider doesn’t allow you to. The best way to understand the service of a particular Pod provider is to read the Terms. There are some support tools like Terms of Service; Didn’t Read to help you understand the small print more easily.

+

Yes, unless the Pod provider doesn’t allow you to. The best way to understand the service of a particular Pod provider is to read the Terms. There are some support tools like Terms of Service; Didn’t Read to help you understand the small print more easily.

More details

The data in Solid Pods is structured according to Linked Data principles by the applications writing them in the first place. Therefore, the Pod Provider is completely neutral regarding the Pod content, and the same data structure should be supported by any Pod Provider. The storage technology picked by each Pod Provider is another question.

@@ -125,7 +125,7 @@

If I join Solid, can I

Each Pod Provider may offer a its own procedure to delete Pods, which means closing the storage space they rented to you. Once your Pods are deleted, you can delete your WebID by closing your account at your Identity Provider. As for Pod Providers, the exact procedure may differ from an Identity Provider to the other. Remember that you need to log in your Identity Provider to have access to your Pods: if you delete your Identity Provider account first, you may lock yourself out of your Pods.

More details -

Technically, you may host your FOAF profile independently from your Identity Provider, and have it point to a WebID you control. In this case, to fully delete your Solid identity, you may want to delete this profile document as well.

+

Technically, you may host your FOAF profile independently from your Identity Provider, and have it point to a WebID you control. In this case, to fully delete your Solid identity, you may want to delete this profile document as well.

@@ -144,7 +144,7 @@

If e

The Bigger Picture

Doesn't the Data Transfer Project fix the data concerns?

-

The Data Transfer Project is a great start. The next step is to build a healthy array of options for users to make their data work for themselves.

+

The Data Transfer Project is a great start. The next step is to build a healthy array of options for users to make their data work for themselves.

How to integrate iOS and Android apps into Solid?

It is possible for developers to take an iOS or Android app and make it Solid compatible by following the standard and supporting documentation.

@@ -153,7 +153,7 @@

How to integrate iOS a

Currently there are no developer kits in development for Android or iOS. If you really want to have an app that can be installed on Android or iOS, you might consider writing it as a progressive web app or writing it as a hybrid app.

Arguably the Semantic Web or Linked Data never took off, so why is Solid working with it?

-

The Semantic Web and Linked Data principles and technologies have always meant to make data more reusable, and to make data independent from applications, which is why it is at the core of Solid.

+

The Semantic Web and Linked Data principles and technologies have always meant to make data more reusable, and to make data independent from applications, which is why it is at the core of Solid.

More details

The Semantic Web and Linked Data technologies are centered on the question of interoperability. By using these technologies, data is provided a context and is made more reusable. In order to achieve one of the goals of Solid and enable applications to share and reuse data without having the control over it, this kind of interoperability is key, and the Semantic Web and Linked Data principles and technologies seemed to be the fittest for this purpose.

@@ -182,7 +182,7 @@

Right to be Forgotten which makes it possible for European citizens to ask for their data to be deleted. You can find template letters and instructions on how to exercise this right here.

+

If you would like to ask for your data to be deleted from other services, In Europe there is a law called the Right to be Forgotten which makes it possible for European citizens to ask for their data to be deleted. See datarequests.org’s sample GDPR erasure-request letter for template letters and instructions on how to exercise this right.

Apps have my data today. If I move this data to Solid, will they still have a copy?

Yes. Today, other people and organisations have a copy of your data, but you may not. If you join Solid and move your data there, it does not prevent the apps already having your data to keep their copy. However, these apps will have to ask for your authorization to access the new data you store in Solid.

@@ -213,7 +213,7 @@

Is it wise to trust people to make ethical decisions on such a complicated issue?

There is a lot of variation in what people feel to be right and wrong, and ultimately it is down to you to decide what to do with your data. Solid does try to help you make informed choices by providing relevant information and by constantly validating the accuracy of that information.

Are apps vulnerable to an "Origin" bypass?

-

The Solid specification warns that the “trusted apps” feature is new and experimental, as it is known that Origin has the common weakness (CWE-346) of “origin validation”.

+

The Solid specification warns that the “trusted apps” feature is new and experimental, as it is known that Origin has the common weakness (CWE-346) of “origin validation”.

Currently when a user gives a Solid web app access to their Pod, the app Origin is added to that user’s list of trusted apps. The app then receives a token that allows it to interact with the Pod on behalf of the user, and only the Origin included with those interactions is verified using the list of trusted apps.

Although standard browsers automatically include the Origin when they send HTTPS requests, a non-browser client is not required to send one. This means anyone with access to the aforementioned Origin-based token can use non-browser clients to bypass Origin validation for the Pod.

Solutions already are planned to revise and replace this experimental use of Origin, in order to evolve security of the trusted app feature.

diff --git a/KNOWN-LINT-ISSUES.md b/KNOWN-LINT-ISSUES.md new file mode 100644 index 000000000..049a5ff33 --- /dev/null +++ b/KNOWN-LINT-ISSUES.md @@ -0,0 +1,142 @@ +# Known lint issues — baseline for solidproject.org + +This document records the lint rules that were **disabled, downgraded, +or had files excluded** when `htmlhint`, `stylelint`, and `pa11y-ci` +were first wired up (commit trio under +`feat/apps-page-redesign`). The goal of linting here is to catch +**new** regressions introduced by PR diffs, not to enforce a clean-up +march on the existing content. + +If you are the `solid-site-a11y-sweeper` (or any other cleanup agent), +this file is your to-do list. + +## htmlhint — `.htmlhintrc` + +Ran against `**/*.html` (excluding `_site/`, `node_modules/`, +`rdfa/`). 72 source files. + +### First-run violation counts + +| Rule | Count | Action | +| -------------------- | ----: | ------------------------------------------------- | +| `doctype-first` | 52 | **Disabled** globally. Reason below. | +| `tag-pair` | 3 | Enabled; offending files ignored. See below. | +| `spec-char-escape` | 3 | Enabled; offending files ignored. See below. | +| `id-unique` | 1 | Enabled; offending file ignored. See below. | +| `attr-lowercase` | 1 | Enabled; offending file ignored. See below. | + +### Rule: `doctype-first` — DISABLED + +Every Jekyll source page begins with a YAML front-matter block: + +``` +--- +permalink: /foo +layout: default +--- +

...

+``` + +htmlhint sees `---` as non-comment content before `` and +flags every such page (52 of 72 source files). The actual +`` lives in `_layouts/default.html` and is emitted +into every built page in `_site/`. Checking it at the source level is +not meaningful; pa11y-ci will exercise the built pages in CI. + +### Ignored files (via `--ignore` in `npm run lint:html`) + +These files have pre-existing markup bugs that the sweeper agent +should fix before we remove them from the ignore list: + +- **`press.html`** — has a stray `
Ruben Verborgh/dd>` (unclosed + `dd`), and a quoted URL with raw `<` / `>` in an `` around + line 215, yielding `tag-pair` + `spec-char-escape` errors. +- **`specification.html`** — unbalanced `

` inside the Panels list + around line 30. +- **`events.html`** — `